react-email
React Email
Overview
Build and send HTML emails using React components. A modern, component-based approach to email development that works across all major email clients by compiling to compatible HTML.
When to Use
- Creating transactional emails (welcome, password reset, order confirmation)
- Building notification or marketing email templates
- Need consistent rendering across Gmail, Outlook, Apple Mail, Yahoo
- Want component reusability and TypeScript support in emails
- Integrating with email providers like Resend, SendGrid, Postmark
When NOT to use:
- Simple plain-text emails
- Emails that don't need cross-client compatibility
- Projects without React/Node.js
Installation
Scaffold a new React Email project:
npx create-email@latest
Equivalent commands: yarn create email, pnpm create email, bun create email
Then install dependencies and start the dev server:
cd react-email-starter
npm install
npm run dev
The server runs at localhost:3000 with a preview interface for templates in the emails folder.
Adding to an Existing Project
Add dependencies to run the email server:
npm install react-email @react-email/preview-server -D -E
npm install @react-email/components react react-dom -E
Add a script to your package.json:
{
"scripts": {
"email:dev": "email dev"
}
}
If the emails are in a different folder, you can specify the directory:
{
"scripts": {
"email:dev": "email dev --dir src/emails"
}
}
Ensure tsconfig.json includes proper JSX support.
Basic Email Template
Create an email component with proper structure using the Tailwind component for styling:
import {
Html,
Head,
Preview,
Body,
Container,
Heading,
Text,
Button,
Tailwind,
pixelBasedPreset,
} from "@react-email/components";
interface WelcomeEmailProps {
name: string;
verificationUrl: string;
}
export default function WelcomeEmail({ name, verificationUrl }: WelcomeEmailProps) {
return (
<Html lang="en">
<Tailwind
config={{
presets: [pixelBasedPreset],
theme: {
extend: {
colors: {
brand: "#007bff",
},
},
},
}}
>
<Head />
<Body className="bg-gray-100 font-sans">
<Preview>Welcome - Verify your email</Preview>
<Container className="mx-auto max-w-xl p-5">
<Heading className="text-2xl text-gray-800">Welcome!</Heading>
<Text className="text-base text-gray-800">Hi {name}, thanks for signing up!</Text>
<Button
href={verificationUrl}
className="bg-brand box-border block rounded px-5 py-3 text-center text-white no-underline"
>
Verify Email
</Button>
</Container>
</Body>
</Tailwind>
</Html>
);
}
WelcomeEmail.PreviewProps = {
name: "John Doe",
verificationUrl: "https://example.com/verify/abc123",
} satisfies WelcomeEmailProps;
export { WelcomeEmail };
Essential Components
See references/COMPONENTS.md for complete component documentation.
Core Structure:
Html- Root wrapper withlangattributeHead- Meta elements, styles, fontsBody- Main content wrapperContainer- Centers content (max-width layout)Section- Layout sectionsRow&Column- Multi-column layouts (table-based, won't stack on mobile)Tailwind- Enables Tailwind CSS utility classes
Content:
Preview- Inbox preview text, place immediately afterBodyopening tagHeading- h1-h6 headingsText- ParagraphsButton- Styled link buttonsLink- HyperlinksImg- Images (use absolute URLs; copy local images to/emails/static/and use dev server BASE_URL in dev, ask user for production BASE_URL)Hr- Horizontal dividers
Specialized:
CodeBlock- Syntax-highlighted codeCodeInline- Inline codeMarkdown- Render markdownFont- Custom web fonts
Behavioral Guidelines
- When re-iterating over code, only update what the user asked for
- If the user asks for media queries, inform them email clients don't support them and suggest alternatives
- Never use template variables (like
{{name}}) directly in TypeScript code - reference props directly
For example, if the user explicitly asks for a variable following the pattern {{variableName}}:
const EmailTemplate = (props) => {
return (
<h1>Hello, {props.variableName}!</h1>
);
}
EmailTemplate.PreviewProps = {
variableName: "{{variableName}}",
};
export default EmailTemplate;
Never write the {{variableName}} pattern directly in the component structure - explain this makes the template invalid.
Styling
See references/STYLING.md for complete styling documentation, including shared Tailwind config patterns for multiple templates.
Key rules:
- Use
pixelBasedPreset- email clients don't supportremunits - Never use flexbox/grid - use
Row/Columncomponents - Never use SVG/WEBP images - use PNG/JPEG only
- Never use media queries (
sm:,md:) or theme selectors (dark:) - Always specify border type (
border-solid,border-dashed) - Always use
box-borderon buttons
Rendering
Convert to HTML
import { render } from "@react-email/components";
import { WelcomeEmail } from "./emails/welcome";
const html = await render(
<WelcomeEmail name="John" verificationUrl="https://example.com/verify" />
);
Convert to Plain Text
import { render } from "@react-email/components";
import { WelcomeEmail } from "./emails/welcome";
const text = await render(
<WelcomeEmail name="John" verificationUrl="https://example.com/verify" />,
{ plainText: true }
);
Sending
React Email supports any email service provider. See references/SENDING.md for details.
Quick example using Resend:
import { Resend } from "resend";
import { WelcomeEmail } from "./emails/welcome";
const resend = new Resend(process.env.RESEND_API_KEY);
const { data, error } = await resend.emails.send({
from: "Acme <onboarding@resend.dev>",
to: ["user@example.com"],
subject: "Welcome to Acme",
react: <WelcomeEmail name="John" verificationUrl="https://example.com/verify" />,
});
if (error) {
console.error("Failed to send:", error);
}
Resend can receive a React component directly. If not included in the call, Resend will automatically include a plain-text version of the email.
Internationalization
See references/I18N.md for complete i18n documentation with examples for next-intl, react-i18next, and react-intl.
Common Mistakes
| Mistake | Fix |
|---|---|
| Using flexbox/grid | Use Row and Column components or tables |
Using rem units |
Use pixelBasedPreset with Tailwind |
| Using SVG images | Use PNG or JPG instead |
| Using media queries (sm:, md:) | Design mobile-first with stacked layouts |
Template vars in JSX ({{name}}) |
Use props: {props.name} |
| Missing border type | Always specify: border-solid, border-dashed, etc. |
| Fixed image dimensions on content images | Use responsive: w-full h-auto (fixed OK for small icons) |
| Emails over 102KB | Gmail clips larger emails - reduce size |
Best Practices
- Test across clients - Gmail, Outlook, Apple Mail, Yahoo. Use Litmus or Email on Acid for precision.
- Keep it responsive - Max-width ~600px, test on mobile.
- Use absolute image URLs - Host on reliable CDN, always include
alttext. If local development, copy toemails/static/. - Provide plain text version - Required for accessibility.
- Add TypeScript types - Define interfaces for all email props.
- Include PreviewProps - For development testing.
- Handle errors - Always check for errors when sending.
- Use verified domains - For production
fromaddresses.
Additional Resources
- React Email Documentation
- React Email GitHub
- Resend Documentation
- Email Client CSS Support
- Component Reference: references/COMPONENTS.md
- Internationalization Guide: references/I18N.md
- Common Patterns: references/PATTERNS.md
- Sending Guide: references/SENDING.md