React Email: The Complete Guide to Building Emails in JSX
React Email lets you write emails with React components instead of fighting with raw HTML tables. Here's everything you need to know — from setup to production deployment.
Why React Email exists
HTML email development has been stuck in 2003. You write <table> layouts, inline every style, and test across a dozen clients that each interpret CSS differently. React Email replaces this with a component-based workflow: you write JSX, and it compiles to the table-based HTML that email clients expect.
The key insight is that React Email components like Row and Column compile to tables automatically — so you get flexbox-like ergonomics without the compatibility issues that come with actual flexbox.
Setup
npm install @react-email/components react-email # or with bun bun add @react-email/components react-email
React Email provides a dev server (email dev) for local preview. For production rendering, use the render() function to compile JSX to an HTML string.
Core components
React Email ships a small set of components that each compile to email-safe HTML. Here's what each one does:
HtmlRoot wrapper. Sets DOCTYPE and xmlns.
HeadContains <meta> tags, <title>, and embedded styles.
BodyWraps all visible content. Accepts style prop for background color.
ContainerCenters content with a max-width. Compiles to a centered <table>.
Row / ColumnTable-based grid. Use instead of flexbox or CSS grid.
SectionGroups content blocks. Renders as a <table> row.
TextParagraph element with email-safe defaults.
ImgImage with required width/height for Outlook. Always include alt text.
LinkAnchor tag with target="_blank" default.
ButtonCall-to-action button using a padded <a> tag for maximum client support.
HrHorizontal rule using a <table> border trick for consistency.
A complete example
Here's a welcome email using all the core components. Note the inline styles — React Email supports style objects, not CSS classes:
import {
Html, Head, Body, Container,
Row, Column, Text, Img, Button, Hr
} from '@react-email/components';
export default function WelcomeEmail({ name }: { name: string }) {
return (
<Html>
<Head />
<Body style={{ backgroundColor: '#f6f6f6', fontFamily: 'Arial, sans-serif' }}>
<Container style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
<Img
src="https://example.com/logo.png"
width="120"
height="36"
alt="Company Logo"
/>
<Text style={{ fontSize: '24px', fontWeight: 'bold', marginTop: '24px' }}>
Welcome, {name}!
</Text>
<Text style={{ color: '#666', lineHeight: '1.6' }}>
We're glad you're here. Here's what you can do next:
</Text>
<Button
href="https://example.com/dashboard"
style={{
backgroundColor: '#000',
color: '#fff',
padding: '12px 24px',
borderRadius: '6px',
textDecoration: 'none',
}}
>
Go to Dashboard
</Button>
<Hr style={{ borderColor: '#eee', margin: '24px 0' }} />
<Text style={{ fontSize: '12px', color: '#999' }}>
You received this because you signed up at example.com
</Text>
</Container>
</Body>
</Html>
);
}Styling rules
Email clients have inconsistent CSS support. Follow these rules to avoid surprises:
- Always use inline styles — Gmail strips
<style>blocks entirely - No flexbox or grid — Outlook ignores both. Use
Row/Columninstead - Set explicit width/height on images — prevents layout shifts in Outlook
- Use web-safe font stacks — custom fonts via @font-face are unsupported in many clients
- Avoid shorthand properties — some clients parse
margin: 10px 20pxdifferently
Conditional rendering
Because React Email is just React, you get conditional rendering and loops for free. This is one of its biggest advantages over raw HTML templates:
// Conditional content based on props
export default function NotificationEmail({
type,
items,
}: {
type: 'welcome' | 'invoice';
items?: { name: string; price: string }[];
}) {
return (
<Html>
<Body>
<Container>
{type === 'welcome' && (
<Text>Welcome to our platform!</Text>
)}
{type === 'invoice' && items && (
<>
<Text>Here's your invoice:</Text>
{items.map((item, i) => (
<Row key={i}>
<Column>{item.name}</Column>
<Column style={{ textAlign: 'right' }}>{item.price}</Column>
</Row>
))}
</>
)}
</Container>
</Body>
</Html>
);
}Testing across clients
The React Email dev server only shows how your email looks in a browser. It won't catch the CSS properties that Outlook silently drops, the styles Gmail strips, or the dark mode transforms Apple Mail applies.
Emailens can take your React Email JSX source directly — it compiles, transforms per-client, and flags every compatibility issue with fix snippets in React Email syntax.
Test your React Email across 15 clients
Paste your JSX and get a compatibility report with React Email-specific fix snippets.
Preview your emailFree plan — 30 previews/day