Skip to main content
Blog
Mar 4, 2026·10 min read

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

Terminal
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:

Html

Root wrapper. Sets DOCTYPE and xmlns.

Head

Contains <meta> tags, <title>, and embedded styles.

Body

Wraps all visible content. Accepts style prop for background color.

Container

Centers content with a max-width. Compiles to a centered <table>.

Row / Column

Table-based grid. Use instead of flexbox or CSS grid.

Section

Groups content blocks. Renders as a <table> row.

Text

Paragraph element with email-safe defaults.

Img

Image with required width/height for Outlook. Always include alt text.

Link

Anchor tag with target="_blank" default.

Button

Call-to-action button using a padded <a> tag for maximum client support.

Hr

Horizontal 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:

welcome-email.tsx
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/Column instead
  • 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 20px differently

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
// 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 email

Free plan, 30 previews/day