Email Templates Pattern
Build beautiful, reusable email templates with React Email that render consistently across all email clients.
Overview#
React Email provides a modern way to build email templates using React components. Templates are rendered to HTML that works across all major email clients, including Gmail, Outlook, and Apple Mail.
When to use:
- Brand-consistent transactional emails
- Complex email layouts
- Reusable email components
- Type-safe email props
- Team collaboration on email design
Key features:
- React component syntax
- Email client compatibility
- Hot-reload development
- Reusable components
- TypeScript support
Code Example#
Basic Template Structure#
1// emails/welcome.tsx
2import {
3 Body,
4 Button,
5 Container,
6 Head,
7 Heading,
8 Html,
9 Preview,
10 Section,
11 Text
12} from '@react-email/components'
13
14interface WelcomeEmailProps {
15 name: string
16 actionUrl: string
17}
18
19export function WelcomeEmail({ name, actionUrl }: WelcomeEmailProps) {
20 return (
21 <Html>
22 <Head />
23 <Preview>Welcome to Our App</Preview>
24 <Body style={main}>
25 <Container style={container}>
26 <Heading style={h1}>Welcome, {name}!</Heading>
27 <Text style={text}>
28 Thanks for signing up. We are excited to have you on board.
29 </Text>
30 <Section style={buttonContainer}>
31 <Button style={button} href={actionUrl}>
32 Get Started
33 </Button>
34 </Section>
35 <Text style={footer}>
36 If you did not create an account, you can ignore this email.
37 </Text>
38 </Container>
39 </Body>
40 </Html>
41 )
42}
43
44const main = {
45 backgroundColor: '#f6f9fc',
46 fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
47}
48
49const container = {
50 backgroundColor: '#ffffff',
51 margin: '0 auto',
52 padding: '40px 20px',
53 maxWidth: '560px'
54}
55
56const h1 = {
57 color: '#1a1a1a',
58 fontSize: '24px',
59 fontWeight: '600',
60 margin: '0 0 20px'
61}
62
63const text = {
64 color: '#4a4a4a',
65 fontSize: '16px',
66 lineHeight: '1.5',
67 margin: '0 0 20px'
68}
69
70const buttonContainer = {
71 textAlign: 'center' as const,
72 margin: '30px 0'
73}
74
75const button = {
76 backgroundColor: '#000000',
77 borderRadius: '6px',
78 color: '#ffffff',
79 fontSize: '16px',
80 fontWeight: '600',
81 padding: '12px 24px',
82 textDecoration: 'none'
83}
84
85const footer = {
86 color: '#898989',
87 fontSize: '14px',
88 margin: '30px 0 0'
89}
90
91export default WelcomeEmailShared Email Layout#
1// emails/components/Layout.tsx
2import {
3 Body,
4 Container,
5 Head,
6 Html,
7 Img,
8 Preview,
9 Section,
10 Text
11} from '@react-email/components'
12
13interface LayoutProps {
14 preview: string
15 children: React.ReactNode
16}
17
18export function EmailLayout({ preview, children }: LayoutProps) {
19 return (
20 <Html>
21 <Head />
22 <Preview>{preview}</Preview>
23 <Body style={main}>
24 <Container style={container}>
25 {/* Header */}
26 <Section style={header}>
27 <Img
28 src={`${process.env.NEXT_PUBLIC_APP_URL}/logo.png`}
29 width="120"
30 height="40"
31 alt="Company Logo"
32 />
33 </Section>
34
35 {/* Content */}
36 <Section style={content}>
37 {children}
38 </Section>
39
40 {/* Footer */}
41 <Section style={footerSection}>
42 <Text style={footerText}>
43 Company Inc, 123 Main St, City, State 12345
44 </Text>
45 <Text style={footerLinks}>
46 <a href="#" style={link}>Unsubscribe</a>
47 {' | '}
48 <a href="#" style={link}>Privacy Policy</a>
49 </Text>
50 </Section>
51 </Container>
52 </Body>
53 </Html>
54 )
55}
56
57const main = {
58 backgroundColor: '#f4f4f5',
59 fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
60}
61
62const container = {
63 margin: '0 auto',
64 maxWidth: '600px'
65}
66
67const header = {
68 backgroundColor: '#ffffff',
69 padding: '20px',
70 textAlign: 'center' as const,
71 borderBottom: '1px solid #e5e7eb'
72}
73
74const content = {
75 backgroundColor: '#ffffff',
76 padding: '40px 20px'
77}
78
79const footerSection = {
80 padding: '20px',
81 textAlign: 'center' as const
82}
83
84const footerText = {
85 color: '#71717a',
86 fontSize: '12px',
87 margin: '0 0 10px'
88}
89
90const footerLinks = {
91 color: '#71717a',
92 fontSize: '12px'
93}
94
95const link = {
96 color: '#3b82f6',
97 textDecoration: 'underline'
98}Invoice Email Template#
1// emails/invoice.tsx
2import {
3 Column,
4 Heading,
5 Hr,
6 Row,
7 Section,
8 Text
9} from '@react-email/components'
10import { EmailLayout } from './components/Layout'
11
12interface InvoiceItem {
13 name: string
14 quantity: number
15 price: number
16}
17
18interface InvoiceEmailProps {
19 invoiceNumber: string
20 date: string
21 items: InvoiceItem[]
22 total: number
23 customerName: string
24}
25
26export function InvoiceEmail({
27 invoiceNumber,
28 date,
29 items,
30 total,
31 customerName
32}: InvoiceEmailProps) {
33 const formatCurrency = (amount: number) =>
34 new Intl.NumberFormat('en-US', {
35 style: 'currency',
36 currency: 'USD'
37 }).format(amount)
38
39 return (
40 <EmailLayout preview={`Invoice #${invoiceNumber}`}>
41 <Heading style={h1}>Invoice #{invoiceNumber}</Heading>
42 <Text style={text}>Hi {customerName},</Text>
43 <Text style={text}>
44 Thank you for your purchase. Here is your invoice:
45 </Text>
46
47 <Section style={invoiceInfo}>
48 <Text style={infoText}>
49 <strong>Invoice Date:</strong> {date}
50 </Text>
51 <Text style={infoText}>
52 <strong>Invoice Number:</strong> {invoiceNumber}
53 </Text>
54 </Section>
55
56 <Hr style={hr} />
57
58 {/* Table Header */}
59 <Row style={tableHeader}>
60 <Column style={colItem}>Item</Column>
61 <Column style={colQty}>Qty</Column>
62 <Column style={colPrice}>Price</Column>
63 </Row>
64
65 {/* Table Rows */}
66 {items.map((item, index) => (
67 <Row key={index} style={tableRow}>
68 <Column style={colItem}>{item.name}</Column>
69 <Column style={colQty}>{item.quantity}</Column>
70 <Column style={colPrice}>
71 {formatCurrency(item.price * item.quantity)}
72 </Column>
73 </Row>
74 ))}
75
76 <Hr style={hr} />
77
78 {/* Total */}
79 <Row style={totalRow}>
80 <Column style={colItem}>
81 <strong>Total</strong>
82 </Column>
83 <Column style={colQty}></Column>
84 <Column style={colPrice}>
85 <strong>{formatCurrency(total)}</strong>
86 </Column>
87 </Row>
88 </EmailLayout>
89 )
90}
91
92const h1 = {
93 fontSize: '24px',
94 fontWeight: '600',
95 color: '#1a1a1a',
96 margin: '0 0 20px'
97}
98
99const text = {
100 fontSize: '16px',
101 color: '#4a4a4a',
102 lineHeight: '1.5',
103 margin: '0 0 20px'
104}
105
106const invoiceInfo = {
107 backgroundColor: '#f9fafb',
108 padding: '16px',
109 borderRadius: '8px',
110 marginBottom: '20px'
111}
112
113const infoText = {
114 fontSize: '14px',
115 color: '#4a4a4a',
116 margin: '0 0 8px'
117}
118
119const hr = {
120 borderColor: '#e5e7eb',
121 margin: '20px 0'
122}
123
124const tableHeader = {
125 backgroundColor: '#f3f4f6',
126 padding: '12px'
127}
128
129const tableRow = {
130 padding: '12px 0',
131 borderBottom: '1px solid #e5e7eb'
132}
133
134const totalRow = {
135 padding: '12px 0'
136}
137
138const colItem = {
139 width: '60%',
140 textAlign: 'left' as const
141}
142
143const colQty = {
144 width: '15%',
145 textAlign: 'center' as const
146}
147
148const colPrice = {
149 width: '25%',
150 textAlign: 'right' as const
151}
152
153export default InvoiceEmailNotification Email with Action#
1// emails/notification.tsx
2import { Button, Heading, Text } from '@react-email/components'
3import { EmailLayout } from './components/Layout'
4
5interface NotificationEmailProps {
6 title: string
7 message: string
8 actionLabel?: string
9 actionUrl?: string
10}
11
12export function NotificationEmail({
13 title,
14 message,
15 actionLabel,
16 actionUrl
17}: NotificationEmailProps) {
18 return (
19 <EmailLayout preview={title}>
20 <Heading style={h1}>{title}</Heading>
21 <Text style={text}>{message}</Text>
22
23 {actionLabel && actionUrl && (
24 <Button style={button} href={actionUrl}>
25 {actionLabel}
26 </Button>
27 )}
28 </EmailLayout>
29 )
30}
31
32const h1 = {
33 fontSize: '20px',
34 fontWeight: '600',
35 color: '#1a1a1a',
36 margin: '0 0 16px'
37}
38
39const text = {
40 fontSize: '16px',
41 color: '#4a4a4a',
42 lineHeight: '1.5',
43 margin: '0 0 24px'
44}
45
46const button = {
47 backgroundColor: '#2563eb',
48 borderRadius: '6px',
49 color: '#ffffff',
50 fontSize: '14px',
51 fontWeight: '600',
52 padding: '10px 20px',
53 textDecoration: 'none'
54}
55
56export default NotificationEmailDevelopment Server#
1# Install React Email CLI
2npm install -D react-email
3
4# Add script to package.json
5{
6 "scripts": {
7 "email:dev": "email dev"
8 }
9}
10
11# Run development server
12npm run email:devRendering Templates#
1// lib/email/render.ts
2import { render } from '@react-email/render'
3import WelcomeEmail from '@/emails/welcome'
4import InvoiceEmail from '@/emails/invoice'
5import NotificationEmail from '@/emails/notification'
6
7type EmailTemplates = {
8 welcome: typeof WelcomeEmail
9 invoice: typeof InvoiceEmail
10 notification: typeof NotificationEmail
11}
12
13const templates: EmailTemplates = {
14 welcome: WelcomeEmail,
15 invoice: InvoiceEmail,
16 notification: NotificationEmail
17}
18
19export async function renderEmail<T extends keyof EmailTemplates>(
20 template: T,
21 props: Parameters<EmailTemplates[T]>[0]
22): Promise<string> {
23 const Template = templates[template]
24 return render(Template(props as any))
25}
26
27// Usage
28const html = await renderEmail('welcome', {
29 name: 'John',
30 actionUrl: 'https://example.com'
31})Usage Instructions#
- Install React Email:
npm install @react-email/components react-email - Create email directory: Add templates to
emails/folder - Build components: Create reusable layout and style components
- Run dev server: Use
npx email devfor live preview - Render and send: Use
render()to convert to HTML for sending
Best Practices#
- Use inline styles - Email clients require inline CSS
- Create shared layouts - Reuse header/footer across templates
- Define style constants - Keep styles consistent and maintainable
- Test extensively - Preview in multiple email clients
- Use TypeScript - Define props interfaces for type safety
- Keep templates simple - Complex layouts break in some clients
- Use tables for layout - More reliable than flexbox/grid
- Provide fallback fonts - Use web-safe font stacks
Related Patterns#
- Transactional Email - Sending emails with Resend
- Email Queues - Background email processing
- Email Tracking - Open and click analytics