Skip to main content
Technology & EngineeringEmail Services234 lines

React Email

Build email templates with React Email. Use this skill when the project needs to

Quick Summary32 lines
You are an email template specialist who builds production email templates using
React Email. React Email lets you write email HTML as React components with
full TypeScript support, a live preview server, and rendering to standards-compliant
HTML that works across all email clients.

## Key Points

- Use the Preview component — it controls inbox preview text
- Always use inline styles — email clients strip `<style>` tags
- Use absolute URLs for all images and links
- Create shared components for header, footer, and buttons
- Test in the dev server across different data scenarios
- Always generate both HTML and plain text versions
- Keep images small — many email clients block images by default
- Using CSS classes or external stylesheets — email clients strip them
- Using relative URLs for images — they break in email clients
- Not including a Preview component — inbox shows raw HTML text
- Building deeply nested layouts — email rendering engines have limits
- Using web-only CSS features (flexbox, grid, CSS variables) in email

## Quick Example

```bash
npm install @react-email/components react-email
npx react-email init  # Optional: scaffolds emails/ directory
```

```bash
npx react-email dev --dir ./emails --port 3001
```
skilldb get email-services-skills/React EmailFull skill: 234 lines
Paste into your CLAUDE.md or agent config

React Email Template Development

You are an email template specialist who builds production email templates using React Email. React Email lets you write email HTML as React components with full TypeScript support, a live preview server, and rendering to standards-compliant HTML that works across all email clients.

Core Philosophy

Components, not string concatenation

Email HTML is notoriously brittle. React Email gives you tested, cross-client components (Button, Container, Column, Image, Link) that generate the right HTML and inline styles. No more debugging Outlook-specific table layouts by hand.

Provider-agnostic rendering

React Email templates render to plain HTML strings. Send through any provider — Resend, SendGrid, Postmark, SES, Nodemailer. The template layer is completely decoupled from the sending layer.

Preview-driven development

React Email includes a dev server that renders templates in the browser with hot reload. Design emails like you design web pages — see changes instantly.

Setup

Install

npm install @react-email/components react-email
npx react-email init  # Optional: scaffolds emails/ directory

Dev server

npx react-email dev --dir ./emails --port 3001

Key Techniques

Basic template

import {
  Html, Head, Body, Container, Section,
  Text, Button, Img, Hr, Preview,
} from '@react-email/components';

interface WelcomeProps {
  name: string;
  dashboardUrl: string;
}

export default function WelcomeEmail({ name, dashboardUrl }: WelcomeProps) {
  return (
    <Html>
      <Head />
      <Preview>Welcome to the platform, {name}</Preview>
      <Body style={main}>
        <Container style={container}>
          <Img src="https://yourdomain.com/logo.png" width={120} height={40} alt="Logo" />
          <Text style={heading}>Welcome, {name}</Text>
          <Text style={paragraph}>
            Your account is ready. Start exploring the dashboard.
          </Text>
          <Button style={button} href={dashboardUrl}>
            Open Dashboard
          </Button>
          <Hr style={hr} />
          <Text style={footer}>
            Questions? Reply to this email.
          </Text>
        </Container>
      </Body>
    </Html>
  );
}

// Inline styles (required for email clients)
const main = { backgroundColor: '#0a0a0f', fontFamily: 'sans-serif' };
const container = { maxWidth: '560px', margin: '0 auto', padding: '32px' };
const heading = { fontSize: '22px', fontWeight: '700', color: '#e8e8ef' };
const paragraph = { fontSize: '15px', lineHeight: '1.6', color: '#c4c4cc' };
const button = {
  backgroundColor: '#d4a843', color: '#08080a', padding: '12px 28px',
  borderRadius: '8px', fontSize: '14px', fontWeight: '600', textDecoration: 'none',
};
const hr = { borderColor: '#222230', margin: '24px 0' };
const footer = { fontSize: '12px', color: '#555' };

Render to HTML

import { render } from '@react-email/render';
import WelcomeEmail from './emails/welcome';

// Static render (for sending)
const html = await render(WelcomeEmail({ name: 'Alice', dashboardUrl: '...' }));

// Plain text render
const text = await render(WelcomeEmail({ name: 'Alice', dashboardUrl: '...' }), {
  plainText: true,
});

Send with any provider

Resend:

import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);

await resend.emails.send({
  from: 'App <noreply@yourdomain.com>',
  to: 'user@example.com',
  subject: 'Welcome',
  html: await render(WelcomeEmail({ name: 'Alice', dashboardUrl: '...' })),
});

Nodemailer:

await transporter.sendMail({
  from: '"App" <noreply@yourdomain.com>',
  to: 'user@example.com',
  subject: 'Welcome',
  html: await render(WelcomeEmail({ name: 'Alice', dashboardUrl: '...' })),
});

Responsive layouts with columns

import { Row, Column } from '@react-email/components';

<Row>
  <Column style={{ width: '50%' }}>
    <Text>Left column</Text>
  </Column>
  <Column style={{ width: '50%' }}>
    <Text>Right column</Text>
  </Column>
</Row>

Conditional content

export default function ReceiptEmail({ plan, amount, isTrial }: Props) {
  return (
    <Html>
      <Body style={main}>
        <Container style={container}>
          <Text style={heading}>
            {isTrial ? 'Trial Started' : 'Payment Confirmed'}
          </Text>
          {!isTrial && (
            <Text style={paragraph}>Amount: {amount}</Text>
          )}
          {isTrial && (
            <Text style={paragraph}>Your trial is active — no charge yet.</Text>
          )}
        </Container>
      </Body>
    </Html>
  );
}

Available Components

ComponentUse for
Html, Head, BodyDocument structure
PreviewPreview text shown in inbox
ContainerCentered content wrapper
SectionContent grouping
Row, ColumnMulti-column layouts
TextParagraphs and headings
LinkHyperlinks
ButtonCall-to-action buttons
ImgImages (use absolute URLs)
HrHorizontal rules
CodeBlock, CodeInlineCode display
HeadingSemantic headings
TailwindTailwind CSS wrapper (utility classes)

Template Organization

emails/
  components/
    header.tsx      # Shared brand header
    footer.tsx      # Shared legal footer
    button.tsx      # Branded CTA button
  auth/
    welcome.tsx
    verify-email.tsx
    password-reset.tsx
  billing/
    receipt.tsx
    payment-failed.tsx
    subscription-canceled.tsx
  product/
    feature-announcement.tsx
    weekly-summary.tsx
  marketing/
    newsletter.tsx

Best Practices

  • Use the Preview component — it controls inbox preview text
  • Always use inline styles — email clients strip <style> tags
  • Use absolute URLs for all images and links
  • Create shared components for header, footer, and buttons
  • Test in the dev server across different data scenarios
  • Always generate both HTML and plain text versions
  • Keep images small — many email clients block images by default

Anti-Patterns

  • Using CSS classes or external stylesheets — email clients strip them
  • Using relative URLs for images — they break in email clients
  • Not including a Preview component — inbox shows raw HTML text
  • Building deeply nested layouts — email rendering engines have limits
  • Using web-only CSS features (flexbox, grid, CSS variables) in email
  • Not testing with empty/missing props — emails should degrade gracefully

Install this skill directly: skilldb add email-services-skills

Get CLI access →