| name | react-email-templates |
| description | React Email component patterns for building responsive email templates using JSX, component composition, and preview servers. Use when creating reusable email components, building responsive templates, setting up email preview environments, or integrating React Email with Resend for dynamic email content. |
| allowed-tools | Read, Write, Bash, Grep |
React Email Templates Skill
Comprehensive patterns and templates for building modern, responsive email templates using React Email components and JSX, with preview server setup and Resend integration.
Use When
- Building reusable email components with React JSX
- Creating responsive email templates that work across clients
- Setting up React Email preview servers for development
- Integrating React Email with Resend for dynamic content
- Implementing welcome, transactional, and marketing emails
- Styling emails with Tailwind CSS via @react-email/components
- Testing email layouts before sending
- Creating component libraries for email templates
Core Concepts
What is React Email
React Email is a library for building responsive, maintainable emails using React components and JSX. It provides:
- JSX Templates: Write email templates as React components
- Built-in Components: Email-safe components (Container, Row, Column, Text, Image, etc.)
- Styling: Tailwind CSS support with safe subset for email clients
- Preview Server: Built-in development server to test email rendering
- Type Safety: Full TypeScript support for email props
- Framework Agnostic: Send emails with Resend, SendGrid, Nodemailer, etc.
Installation
npm install react-email @react-email/components
# or
yarn add react-email @react-email/components
Package Structure
// Core React Email
import { render } from 'react-email';
// Components
import {
Body,
Button,
Column,
Container,
Head,
Hr,
Html,
Img,
Link,
Preview,
Row,
Section,
Text,
Font,
Head as EmailHead,
} from '@react-email/components';
Core Patterns
1. Basic Email Component
Simple, responsive email template structure:
import { Body, Container, Head, Html, Preview, Section, Text } from '@react-email/components';
interface BasicEmailProps {
userName: string;
message: string;
}
export const BasicEmail: React.FC<BasicEmailProps> = ({ userName, message }) => {
return (
<Html>
<Head />
<Preview>Welcome to our service, {userName}!</Preview>
<Body style={main}>
<Container style={container}>
<Section>
<Text style={heading}>Welcome, {userName}!</Text>
<Text style={paragraph}>{message}</Text>
</Section>
</Container>
</Body>
</Html>
);
};
const main = {
backgroundColor: '#f6f9fc',
fontFamily: '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
};
const container = {
backgroundColor: '#ffffff',
margin: '0 auto',
padding: '20px 0 48px',
marginBottom: '64px',
};
const heading = {
fontSize: '32px',
lineHeight: '1.3',
fontWeight: '700',
color: '#1f2937',
};
const paragraph = {
fontSize: '16px',
lineHeight: '26px',
color: '#525252',
};
2. Welcome Email Template
Complete welcome email with branding, CTA button, and footer:
import {
Body,
Button,
Column,
Container,
Head,
Hr,
Html,
Img,
Link,
Preview,
Row,
Section,
Text,
} from '@react-email/components';
interface WelcomeEmailProps {
userName: string;
userEmail: string;
activationUrl: string;
companyName?: string;
}
export const WelcomeEmail: React.FC<WelcomeEmailProps> = ({
userName,
userEmail,
activationUrl,
companyName = 'Our Company',
}) => {
return (
<Html>
<Head>
<Font
fontFamily="Geist"
fallbackFontFamily="Verdana"
webFont={{
url: 'https://cdn.jsdelivr.net/npm/geist-font@1.0.0/dist/geist-mono.woff2',
format: 'woff2',
}}
/>
</Head>
<Preview>Welcome to {companyName}, {userName}!</Preview>
<Body style={main}>
<Container style={container}>
{/* Header with Logo */}
<Section style={header}>
<Row>
<Column>
<Img src={`https://${process.env.VERCEL_URL}/logo.png`} width="40" height="40" alt={companyName} />
</Column>
<Column style={{ paddingLeft: '8px' }}>
<Text style={headerText}>{companyName}</Text>
</Column>
</Row>
</Section>
{/* Main Content */}
<Section style={content}>
<Text style={heading}>Welcome to {companyName}!</Text>
<Text style={paragraph}>
Hi {userName},
</Text>
<Text style={paragraph}>
Thank you for signing up. We're excited to have you on board. To get started, please verify your email address by clicking the button below.
</Text>
{/* CTA Button */}
<Section style={buttonContainer}>
<Button style={button} href={activationUrl}>
Verify Email Address
</Button>
</Section>
<Text style={paragraph}>
This link expires in 24 hours. If you didn't create this account, you can ignore this email.
</Text>
</Section>
<Hr style={hr} />
{/* Footer */}
<Section style={footer}>
<Row>
<Column>
<Text style={footerText}>
{companyName} Inc. | {userEmail}
</Text>
<Text style={footerText}>
<Link href="https://example.com/unsubscribe" style={link}>
Unsubscribe
</Link>
{' | '}
<Link href="https://example.com/preferences" style={link}>
Preferences
</Link>
</Text>
</Column>
</Row>
</Section>
</Container>
</Body>
</Html>
);
};
// Styles
const main = {
backgroundColor: '#f6f9fc',
fontFamily: 'Geist, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
};
const container = {
backgroundColor: '#ffffff',
margin: '0 auto',
padding: '20px 0 48px',
marginBottom: '64px',
};
const header = {
padding: '20px 0',
borderBottom: '1px solid #e5e7eb',
};
const headerText = {
fontSize: '18px',
fontWeight: '700',
color: '#1f2937',
};
const content = {
padding: '32px 0',
};
const heading = {
fontSize: '28px',
lineHeight: '1.3',
fontWeight: '700',
color: '#1f2937',
marginBottom: '16px',
};
const paragraph = {
fontSize: '15px',
lineHeight: '1.6',
color: '#525252',
marginBottom: '12px',
};
const buttonContainer = {
paddingTop: '16px',
paddingBottom: '16px',
};
const button = {
backgroundColor: '#2563eb',
borderRadius: '4px',
color: '#ffffff',
fontSize: '15px',
fontWeight: '600',
padding: '12px 20px',
textDecoration: 'none',
textAlign: 'center' as const,
};
const hr = {
borderColor: '#e5e7eb',
margin: '0',
};
const footer = {
paddingTop: '24px',
paddingBottom: '24px',
borderTop: '1px solid #e5e7eb',
};
const footerText = {
fontSize: '12px',
color: '#6b7280',
margin: '0 0 4px 0',
};
const link = {
color: '#2563eb',
textDecoration: 'underline',
};
3. Transactional Email - Order Confirmation
Order confirmation email with itemized details and status tracking:
import {
Body,
Button,
Column,
Container,
Head,
Html,
Img,
Link,
Preview,
Row,
Section,
Text,
} from '@react-email/components';
interface OrderConfirmationProps {
orderNumber: string;
customerName: string;
orderDate: string;
items: Array<{
name: string;
quantity: number;
price: number;
}>;
subtotal: number;
tax: number;
shipping: number;
total: number;
trackingUrl: string;
}
export const OrderConfirmation: React.FC<OrderConfirmationProps> = ({
orderNumber,
customerName,
orderDate,
items,
subtotal,
tax,
shipping,
total,
trackingUrl,
}) => {
return (
<Html>
<Head />
<Preview>Order {orderNumber} confirmed</Preview>
<Body style={main}>
<Container style={container}>
{/* Header */}
<Section style={header}>
<Text style={heading}>Order Confirmation</Text>
<Text style={subHeading}>Order #{orderNumber}</Text>
</Section>
{/* Customer Info */}
<Section style={section}>
<Text style={sectionHeading}>Thank you, {customerName}!</Text>
<Text style={paragraph}>
Your order has been received and will be processed shortly.
</Text>
<Text style={paragraph}>
<strong>Order Date:</strong> {orderDate}
</Text>
</Section>
{/* Order Items */}
<Section style={section}>
<Text style={sectionHeading}>Order Items</Text>
<table style={table}>
<tbody>
{items.map((item, idx) => (
<tr key={idx}>
<td style={tableCell}>{item.name}</td>
<td style={{ ...tableCell, textAlign: 'center' }}>{item.quantity}</td>
<td style={{ ...tableCell, textAlign: 'right' }}>
${(item.price * item.quantity).toFixed(2)}
</td>
</tr>
))}
<tr>
<td colSpan={2} style={tableCellRight}>
<strong>Subtotal:</strong>
</td>
<td style={tableCellRight}>${subtotal.toFixed(2)}</td>
</tr>
<tr>
<td colSpan={2} style={tableCellRight}>
<strong>Tax:</strong>
</td>
<td style={tableCellRight}>${tax.toFixed(2)}</td>
</tr>
<tr>
<td colSpan={2} style={tableCellRight}>
<strong>Shipping:</strong>
</td>
<td style={tableCellRight}>${shipping.toFixed(2)}</td>
</tr>
<tr style={totalRow}>
<td colSpan={2} style={{ ...tableCellRight, color: '#ffffff' }}>
<strong>Total:</strong>
</td>
<td style={{ ...tableCellRight, color: '#ffffff' }}>
<strong>${total.toFixed(2)}</strong>
</td>
</tr>
</tbody>
</table>
</Section>
{/* Tracking */}
<Section style={section}>
<Button style={button} href={trackingUrl}>
Track Your Order
</Button>
</Section>
{/* Footer */}
<Section style={footer}>
<Text style={footerText}>
Questions? Contact us at support@example.com
</Text>
</Section>
</Container>
</Body>
</Html>
);
};
// Styles
const main = {
backgroundColor: '#f6f9fc',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
};
const container = {
backgroundColor: '#ffffff',
margin: '0 auto',
padding: '20px 0 48px',
marginBottom: '64px',
};
const header = {
padding: '20px 24px',
backgroundColor: '#f3f4f6',
borderBottom: '1px solid #e5e7eb',
};
const heading = {
fontSize: '24px',
fontWeight: '700',
color: '#1f2937',
margin: '0 0 4px 0',
};
const subHeading = {
fontSize: '14px',
color: '#6b7280',
margin: '0',
};
const section = {
padding: '24px',
borderBottom: '1px solid #e5e7eb',
};
const sectionHeading = {
fontSize: '16px',
fontWeight: '600',
color: '#1f2937',
marginBottom: '12px',
};
const paragraph = {
fontSize: '14px',
lineHeight: '1.5',
color: '#525252',
margin: '0 0 8px 0',
};
const table = {
width: '100%',
borderCollapse: 'collapse' as const,
};
const tableCell = {
padding: '12px',
borderBottom: '1px solid #e5e7eb',
fontSize: '14px',
color: '#525252',
};
const tableCellRight = {
...tableCell,
textAlign: 'right' as const,
};
const totalRow = {
backgroundColor: '#2563eb',
};
const button = {
backgroundColor: '#2563eb',
borderRadius: '4px',
color: '#ffffff',
fontSize: '15px',
fontWeight: '600',
padding: '12px 20px',
textDecoration: 'none',
textAlign: 'center' as const,
display: 'block' as const,
width: 'fit-content',
};
const footer = {
padding: '24px',
textAlign: 'center' as const,
};
const footerText = {
fontSize: '12px',
color: '#6b7280',
};
4. Password Reset Email
Secure password reset flow with time-limited link:
import { Body, Button, Container, Head, Html, Preview, Section, Text } from '@react-email/components';
interface PasswordResetProps {
userName: string;
resetUrl: string;
expiresIn: string; // e.g., "24 hours"
}
export const PasswordReset: React.FC<PasswordResetProps> = ({
userName,
resetUrl,
expiresIn,
}) => {
return (
<Html>
<Head />
<Preview>Reset your password</Preview>
<Body style={main}>
<Container style={container}>
<Section style={content}>
<Text style={heading}>Password Reset Request</Text>
<Text style={paragraph}>
Hi {userName},
</Text>
<Text style={paragraph}>
We received a request to reset the password associated with your account. Click the button below to reset it.
</Text>
<Section style={buttonContainer}>
<Button style={button} href={resetUrl}>
Reset Password
</Button>
</Section>
<Text style={warningText}>
This link expires in {expiresIn}. If you didn't request this, you can ignore this email.
</Text>
<Text style={paragraph}>
For security reasons, never share this link with anyone.
</Text>
</Section>
</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',
};
const content = {
padding: '20px',
};
const heading = {
fontSize: '24px',
fontWeight: '700',
color: '#1f2937',
marginBottom: '20px',
};
const paragraph = {
fontSize: '15px',
lineHeight: '1.6',
color: '#525252',
marginBottom: '16px',
};
const buttonContainer = {
paddingTop: '20px',
paddingBottom: '20px',
};
const button = {
backgroundColor: '#dc2626',
borderRadius: '4px',
color: '#ffffff',
fontSize: '15px',
fontWeight: '600',
padding: '12px 24px',
textDecoration: 'none',
};
const warningText = {
fontSize: '13px',
color: '#d97706',
fontStyle: 'italic',
marginBottom: '16px',
};
5. Marketing Email - Newsletter
Newsletter template with featured content, sections, and unsubscribe:
import {
Body,
Column,
Container,
Head,
Html,
Img,
Link,
Preview,
Row,
Section,
Text,
} from '@react-email/components';
interface NewsletterProps {
recipientName: string;
featuredTitle: string;
featuredImage: string;
featuredUrl: string;
articles: Array<{
title: string;
excerpt: string;
url: string;
image?: string;
}>;
unsubscribeUrl: string;
}
export const Newsletter: React.FC<NewsletterProps> = ({
recipientName,
featuredTitle,
featuredImage,
featuredUrl,
articles,
unsubscribeUrl,
}) => {
return (
<Html>
<Head />
<Preview>Latest from our community</Preview>
<Body style={main}>
<Container style={container}>
{/* Header */}
<Section style={header}>
<Text style={headerTitle}>Our Weekly Newsletter</Text>
</Section>
{/* Greeting */}
<Section style={content}>
<Text style={greeting}>Hi {recipientName},</Text>
<Text style={paragraph}>
Here's what's happening in our community this week.
</Text>
</Section>
{/* Featured Article */}
<Section style={featured}>
<Img src={featuredImage} width="100%" height="auto" alt={featuredTitle} />
<Section style={featuredContent}>
<Text style={featuredTitle}>{featuredTitle}</Text>
<Link href={featuredUrl} style={link}>
Read More →
</Link>
</Section>
</Section>
{/* Articles Grid */}
<Section style={articlesSection}>
{articles.map((article, idx) => (
<Row key={idx}>
<Column style={articleColumn}>
{article.image && (
<Img src={article.image} width="100%" height="auto" alt={article.title} />
)}
<Text style={articleTitle}>{article.title}</Text>
<Text style={articleExcerpt}>{article.excerpt}</Text>
<Link href={article.url} style={link}>
Read More →
</Link>
</Column>
</Row>
))}
</Section>
{/* Footer */}
<Section style={footer}>
<Text style={footerText}>
You're receiving this because you're subscribed to our newsletter.
</Text>
<Text style={footerText}>
<Link href={unsubscribeUrl} style={link}>
Unsubscribe
</Link>
</Text>
</Section>
</Container>
</Body>
</Html>
);
};
const main = {
backgroundColor: '#f9fafb',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
};
const container = {
backgroundColor: '#ffffff',
margin: '0 auto',
padding: '20px 0',
marginBottom: '64px',
};
const header = {
backgroundColor: '#1f2937',
color: '#ffffff',
padding: '32px 20px',
textAlign: 'center' as const,
};
const headerTitle = {
fontSize: '28px',
fontWeight: '700',
margin: '0',
color: '#ffffff',
};
const content = {
padding: '20px',
};
const greeting = {
fontSize: '18px',
fontWeight: '600',
color: '#1f2937',
marginBottom: '12px',
};
const paragraph = {
fontSize: '15px',
lineHeight: '1.6',
color: '#525252',
};
const featured = {
margin: '20px 0',
};
const featuredContent = {
padding: '20px',
backgroundColor: '#f3f4f6',
};
const featuredTitle = {
fontSize: '20px',
fontWeight: '700',
color: '#1f2937',
marginBottom: '12px',
};
const articlesSection = {
padding: '20px',
};
const articleColumn = {
padding: '12px',
borderRight: '1px solid #e5e7eb',
};
const articleTitle = {
fontSize: '16px',
fontWeight: '600',
color: '#1f2937',
marginTop: '12px',
marginBottom: '8px',
};
const articleExcerpt = {
fontSize: '14px',
lineHeight: '1.5',
color: '#6b7280',
marginBottom: '12px',
};
const link = {
color: '#2563eb',
textDecoration: 'none',
fontWeight: '600',
};
const footer = {
backgroundColor: '#f9fafb',
padding: '24px 20px',
textAlign: 'center' as const,
borderTop: '1px solid #e5e7eb',
};
const footerText = {
fontSize: '12px',
color: '#6b7280',
margin: '0 0 8px 0',
};
Preview Server Setup
Development Server
Create a preview server for testing email templates:
// emails/preview.tsx
import { render } from 'react-email';
import { WelcomeEmail } from './welcome';
async function main() {
const emailHtml = render(
<WelcomeEmail
userName="John Doe"
userEmail="john@example.com"
activationUrl="https://example.com/activate/abc123"
companyName="Example Co"
/>,
);
console.log(emailHtml);
}
main().catch(console.error);
Resend Integration
// Send email with React Email and Resend
import { render } from 'react-email';
import { Resend } from 'resend';
import { WelcomeEmail } from './emails/welcome';
const resend = new Resend('your_resend_key_here');
async function sendWelcomeEmail(userName: string, userEmail: string) {
const html = render(
<WelcomeEmail
userName={userName}
userEmail={userEmail}
activationUrl={`https://app.example.com/activate/${generateToken()}`}
companyName="Your Company"
/>,
);
const { data, error } = await resend.emails.send({
from: 'onboarding@example.com',
to: userEmail,
subject: `Welcome to Your Company, ${userName}!`,
html: html,
});
if (error) {
console.error('Failed to send email:', error);
return null;
}
return data;
}
Responsive Design Patterns
Mobile-First Approach
import { Container, Section, Column, Row } from '@react-email/components';
// Responsive container
const ResponsiveSection = () => (
<Section>
<Row>
<Column style={{ width: '100%', maxWidth: '600px' }}>
{/* Content scales on mobile */}
</Column>
</Row>
</Section>
);
// Key CSS properties for email-safe styling
const responsiveStyles = {
// Use pixel values or percentages
width: '100%',
maxWidth: '600px',
// Safe font stacks
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
// Avoid absolute positioning
margin: '0 auto',
padding: '20px',
// Safe colors (avoid rgba for older clients)
backgroundColor: '#ffffff',
color: '#1f2937',
};
Best Practices
Component Organization
// emails/components/Header.tsx
export const Header: React.FC<{ title: string }> = ({ title }) => (
<Section style={headerStyle}>
<Text style={heading}>{title}</Text>
</Section>
);
// emails/components/Footer.tsx
export const Footer: React.FC = () => (
<Section style={footerStyle}>
<Text style={footerText}>© 2024 Company. All rights reserved.</Text>
</Section>
);
// emails/welcome.tsx
import { Header } from './components/Header';
import { Footer } from './components/Footer';
export const WelcomeEmail = () => (
<Html>
<Body>
<Container>
<Header title="Welcome!" />
{/* Content */}
<Footer />
</Container>
</Body>
</Html>
);
Email Testing
- Test across major email clients: Gmail, Outlook, Apple Mail
- Use Resend's preview feature to verify rendering
- Always provide text fallback for images
- Test on mobile and desktop viewports
- Validate links and button functionality
- Check for accessibility (alt text, contrast ratios)
Performance Tips
- Minimize inline CSS
- Use safe color values (#hex)
- Optimize images (JPEG for photos, PNG for graphics)
- Keep emails under 25MB total
- Use base64 only for small images
- Avoid JavaScript (emails don't support it)
Security Best Practices
- Never embed secrets in email HTML
- Validate all dynamic content
- Use HTTPS for all links
- Implement authentication for preview URLs
- Sanitize user-generated content
- Use environment variables for API keys
Examples Directory Structure
welcome-email/- Complete welcome sequence templatestransactional/- Order confirmation, password reset, invoicesmarketing/- Newsletters, promotional campaigns, digests
See individual example README files for complete implementation details.
Related Skills
- email-delivery - Sending emails with Resend API
- email-validation - Email address and template validation
- email-webhooks - Delivery events and bounce handling
Resources
- React Email Documentation
- React Email Components
- Resend React Email Integration
- Email Client CSS Support
- MJML Email Framework - Alternative template approach
Security Notes
- API keys must be stored in environment variables (never hardcoded)
- Use
RESEND_API_KEYfrom secure secret management - Sanitize all user input before rendering in emails
- Validate preview URLs to prevent unauthorized access
- Log email rendering errors for debugging
- Test HTML sanitization for email content