React Email lets you build transactional emails with the same component model you use for your UI. No more wrangling raw HTML tables or debugging inline styles across dozens of email clients. In this guide, you'll go from zero to sending your first email in a Next.js project.
Prerequisites
Before you start, make sure you have:
- Node.js 18+ installed
- A Next.js 14 or 15 project (App Router or Pages Router both work)
- An email sending service account — Resend, SendGrid, Postmark, or AWS SES all work
Step 1: Install React Email
Add the core packages to your project. react-email gives you the dev server for previewing templates, and @react-email/components provides the cross-client-safe primitives.
npm install react-email @react-email/componentsIf you're using Resend as your sending service (recommended for Next.js projects), add it too:
npm install resendStep 2: Create Your First Template
Create an emails/ directory at the root of your project. React Email looks for templates here by default.
import {
Html,
Head,
Preview,
Body,
Container,
Section,
Text,
Button,
Hr,
} from "@react-email/components";
type WelcomeEmailProps = {
name: string;
loginUrl: string;
};
export default function WelcomeEmail({ name, loginUrl }: WelcomeEmailProps) {
return (
<Html>
<Head />
<Preview>Welcome to our platform, {name}!</Preview>
<Body style={main}>
<Container style={container}>
<Text style={heading}>Welcome, {name}!</Text>
<Text style={paragraph}>
Thanks for signing up. Your account is ready to go.
</Text>
<Section style={buttonContainer}>
<Button style={button} href={loginUrl}>
Go to Dashboard
</Button>
</Section>
<Hr style={hr} />
<Text style={footer}>
If you didn't create this account, you can ignore this email.
</Text>
</Container>
</Body>
</Html>
);
}
const main = {
backgroundColor: "#f6f9fc",
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
};
const container = {
backgroundColor: "#ffffff",
margin: "0 auto",
padding: "40px 20px",
maxWidth: "560px",
};
const heading = {
fontSize: "24px",
fontWeight: "600" as const,
color: "#1a1a1a",
margin: "0 0 16px",
};
const paragraph = {
fontSize: "16px",
lineHeight: "26px",
color: "#484848",
};
const buttonContainer = {
textAlign: "center" as const,
margin: "24px 0",
};
const button = {
backgroundColor: "#000000",
borderRadius: "6px",
color: "#ffffff",
fontSize: "14px",
fontWeight: "600" as const,
textDecoration: "none",
padding: "12px 24px",
};
const hr = {
borderColor: "#e6ebf1",
margin: "24px 0",
};
const footer = {
fontSize: "13px",
color: "#8898aa",
};<style> tags. The component primitives handle the cross-client quirks for you.Step 3: Preview Your Template
Add the preview script to your package.json:
{
"scripts": {
"email": "email dev"
}
}Run npm run email and open localhost:3000. You'll see a live preview of your template with hot reload. Change props, tweak styles, and see results instantly.
Step 4: Send Your First Email
Create an API route that renders the template and sends it through your email provider. Here's how it looks with Resend:
import { Resend } from "resend";
import WelcomeEmail from "@/emails/welcome";
const resend = new Resend(process.env.RESEND_API_KEY);
export async function POST(request: Request) {
const { name, email, loginUrl } = await request.json();
const { data, error } = await resend.emails.send({
from: "Your App <hello@yourapp.com>",
to: email,
subject: `Welcome to our platform, ${name}!`,
react: WelcomeEmail({ name, loginUrl }),
});
if (error) {
return Response.json({ error }, { status: 500 });
}
return Response.json({ id: data?.id });
}react property on the send call renders your component to HTML automatically. No need to call render() yourself.Recommended Project Structure
As your template collection grows, organize files by email type. This scales cleanly to dozens of templates:
emails/
├── components/ # Shared email components (headers, footers, buttons)
│ ├── email-header.tsx
│ ├── email-footer.tsx
│ └── email-button.tsx
├── onboarding/
│ ├── welcome.tsx
│ └── verification.tsx
├── billing/
│ ├── invoice.tsx
│ └── payment-failed.tsx
└── notifications/
├── team-invite.tsx
└── usage-alert.tsxShared Components
Extract repeated elements (logo headers, footer links, CTA buttons) into shared components. This keeps your templates DRY and ensures brand consistency across every email you send.
import { Img, Section } from "@react-email/components";
export function EmailHeader({ logoUrl }: { logoUrl: string }) {
return (
<Section style={{ textAlign: "center", padding: "24px 0" }}>
<Img src={logoUrl} width="120" height="36" alt="Logo" />
</Section>
);
}Recommended
SaaS Essentials Pack
21+ Templates · 60+ Variations. One-time purchase, lifetime updates.
Step 5: Test Across Email Clients
Before going to production, test your templates across major clients. Here's a practical testing checklist:
- Gmail (web + mobile) — strips most CSS, tests your inline styles
- Outlook (desktop) — uses Word rendering engine, the toughest client to support
- Apple Mail — generally great rendering, good baseline
- Yahoo Mail — aggressive CSS stripping, worth testing
- Dark mode — check that colors remain readable when clients invert backgrounds
Next Steps
With your setup working, here are some areas to explore next:
- Add TypeScript props to every template for type-safe data passing
- Set up preview data for the React Email dev server so you can iterate without sending real emails
- Build a shared design system for your emails (colors, typography, spacing tokens)
- Integrate with your authentication flow (welcome emails on signup, password resets, magic links)
Building production email templates from scratch takes time — especially getting cross-client rendering right. Pre-built templates can save you weeks and give you a solid starting point that's already been tested across every major email client.