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 21 clients
Paste your JSX and get a compatibility report with React Email-specific fix snippets.
Preview your emailFree plan, 30 previews/day