React Email builds transactional email templates using React components. The @react-email/components package provides email-compatible primitives — Html, Head, Body, Section, Row, Column, Button, Text, and Img — that render to inbox-compatible HTML with correct inline styles. render(Template, props) converts a React component to an HTML string and optional plain-text fallback. The React Email CLI starts a local preview server showing templates across email client previews. @react-email/tailwind applies Tailwind classes as inline styles automatically. Resend, SendGrid, Postmark, and Nodemailer accept the rendered HTML string directly. Claude Code generates React Email templates for order confirmations, shipping notifications, password resets, and drip sequence emails with cross-client compatible markup.
CLAUDE.md for React Email
## React Email Stack
- Version: @react-email/components >= 0.0.22, react-email >= 3.0
- Render: render(Template, props) → HTML string — pass to email provider
- Layout: Html > Head > Preview > Body > Container > Section > Row > Column
- Tailwind: <Tailwind config={theme}> wrapper — converts tw classes to inline styles
- Preview: npx react-email dev — live preview at localhost:3000
- Test: npx react-email export — exports HTML for email client testing
- Send: Resend, SendGrid, Nodemailer — all accept HTML string from render()
Order Confirmation Template
// emails/order-confirmation.tsx — React Email template
import {
Html,
Head,
Preview,
Body,
Container,
Section,
Row,
Column,
Text,
Button,
Img,
Hr,
Link,
} from "@react-email/components"
import { Tailwind } from "@react-email/tailwind"
interface OrderConfirmationProps {
customerName: string
orderId: string
orderUrl: string
items: {
name: string
quantity: number
priceCents: number
imageUrl?: string
}[]
subtotalCents: number
shippingCents: number
totalCents: number
estimatedDelivery: string
shippingAddress: {
line1: string
city: string
country: string
}
}
export function OrderConfirmation({
customerName,
orderId,
orderUrl,
items,
subtotalCents,
shippingCents,
totalCents,
estimatedDelivery,
shippingAddress,
}: OrderConfirmationProps) {
return (
<Html>
<Head />
<Preview>Order #{orderId.slice(-8)} confirmed — estimated delivery {estimatedDelivery}</Preview>
<Tailwind>
<Body className="bg-gray-50 font-sans">
<Container className="mx-auto py-8 px-4 max-w-xl">
{/* Header */}
<Section className="text-center mb-8">
<Img
src="https://example.com/logo.png"
width="140"
height="40"
alt="MyStore"
className="mx-auto"
/>
</Section>
{/* Confirmation */}
<Section className="bg-white rounded-lg p-8 mb-6 shadow-sm">
<Text className="text-2xl font-bold text-gray-900 mb-2">
Order Confirmed!
</Text>
<Text className="text-gray-500 text-sm mb-6">
Hi {customerName}, thanks for your order. We'll notify you when it ships.
</Text>
<Row>
<Column>
<Text className="text-xs text-gray-400 uppercase">Order Number</Text>
<Text className="text-sm font-medium text-gray-900">
#{orderId.slice(-8)}
</Text>
</Column>
<Column>
<Text className="text-xs text-gray-400 uppercase">Est. Delivery</Text>
<Text className="text-sm font-medium text-gray-900">
{estimatedDelivery}
</Text>
</Column>
<Column>
<Text className="text-xs text-gray-400 uppercase">Ship To</Text>
<Text className="text-sm font-medium text-gray-900">
{shippingAddress.city}, {shippingAddress.country}
</Text>
</Column>
</Row>
<Button
href={orderUrl}
className="bg-blue-600 text-white px-6 py-3 rounded-md text-sm font-medium mt-6 block text-center"
>
View Order Details
</Button>
</Section>
{/* Order Items */}
<Section className="bg-white rounded-lg p-6 mb-6 shadow-sm">
<Text className="font-semibold text-gray-900 mb-4">Order Summary</Text>
{items.map((item, i) => (
<Row key={i} className="mb-4">
{item.imageUrl && (
<Column className="w-16 pr-4">
<Img
src={item.imageUrl}
width="48"
height="48"
alt={item.name}
className="rounded"
/>
</Column>
)}
<Column>
<Text className="text-sm font-medium text-gray-900 mb-0">
{item.name}
</Text>
<Text className="text-xs text-gray-500 mt-0">
Qty: {item.quantity}
</Text>
</Column>
<Column className="text-right">
<Text className="text-sm text-gray-900">
${((item.priceCents * item.quantity) / 100).toFixed(2)}
</Text>
</Column>
</Row>
))}
<Hr className="my-4 border-gray-200" />
<Row>
<Column><Text className="text-sm text-gray-500">Subtotal</Text></Column>
<Column className="text-right">
<Text className="text-sm text-gray-900">${(subtotalCents / 100).toFixed(2)}</Text>
</Column>
</Row>
<Row>
<Column><Text className="text-sm text-gray-500">Shipping</Text></Column>
<Column className="text-right">
<Text className="text-sm text-gray-900">
{shippingCents === 0 ? "Free" : `$${(shippingCents / 100).toFixed(2)}`}
</Text>
</Column>
</Row>
<Row>
<Column><Text className="text-base font-bold text-gray-900">Total</Text></Column>
<Column className="text-right">
<Text className="text-base font-bold text-gray-900">${(totalCents / 100).toFixed(2)}</Text>
</Column>
</Row>
</Section>
{/* Footer */}
<Section className="text-center">
<Text className="text-xs text-gray-400">
Questions? <Link href="mailto:[email protected]" className="text-blue-600">[email protected]</Link>
</Text>
<Text className="text-xs text-gray-400">
123 Main St, San Francisco, CA 94102
</Text>
</Section>
</Container>
</Body>
</Tailwind>
</Html>
)
}
// Default props for preview
OrderConfirmation.PreviewProps = {
customerName: "Alice Smith",
orderId: "ord-abc123def456",
orderUrl: "https://example.com/orders/ord-abc123def456",
items: [
{ name: "Widget Pro", quantity: 1, priceCents: 2999 },
{ name: "Widget Basic", quantity: 2, priceCents: 999 },
],
subtotalCents: 4997,
shippingCents: 0,
totalCents: 4997,
estimatedDelivery: "Jan 25–28",
shippingAddress: { line1: "123 Main St", city: "San Francisco", country: "US" },
} satisfies OrderConfirmationProps
export default OrderConfirmation
Sending with Resend
// lib/email.ts — render and send with Resend
import { Resend } from "resend"
import { render } from "@react-email/render"
import { OrderConfirmation } from "./emails/order-confirmation"
import { ShippingNotification } from "./emails/shipping-notification"
import { PasswordReset } from "./emails/password-reset"
const resend = new Resend(process.env.RESEND_API_KEY!)
export async function sendOrderConfirmation(
to: string,
props: React.ComponentProps<typeof OrderConfirmation>
) {
const html = await render(<OrderConfirmation {...props} />)
const text = await render(<OrderConfirmation {...props} />, { plainText: true })
return resend.emails.send({
from: "[email protected]",
to,
subject: `Order #${props.orderId.slice(-8)} Confirmed`,
html,
text,
})
}
export async function sendShippingNotification(
to: string,
props: React.ComponentProps<typeof ShippingNotification>
) {
const html = await render(<ShippingNotification {...props} />)
return resend.emails.send({
from: "[email protected]",
to,
subject: `Your order has shipped!`,
html,
})
}
package.json Email Preview
{
"scripts": {
"email:dev": "react-email dev --dir emails --port 3001",
"email:export": "react-email export --outDir ./email-previews"
},
"devDependencies": {
"react-email": "^3.0.5"
}
}
For the MJML alternative markup language for HTML emails that compiles to cross-client-compatible HTML without the React component model, see the email template patterns. For the Mailchimp transactional API (Mandrill) or Postmark equivalents when extensive email analytics, A/B testing deliverability infrastructure, or suppression list management are needed beyond basic send, the Resend guide covers provider comparison. The Claude Skills 360 bundle includes React Email skill sets covering component templates, Tailwind styling, and Resend integration. Start with the free tier to try email template generation.