Email Template Development
This skill guides the development of email templates using React Email with Tailwind CSS.
When to use this skill
- Creating new email templates
- Modifying existing email templates
- Testing email rendering in different clients
- Debugging email template styling
- Compiling React Email to production HTML
Quick Commands
make dev-emails # Start React Email dev server (http://localhost:3001)
make build-emails # Compile React Email to HTML with Jinja2 variables
Development Workflow
1. Start Development Server
make dev-emails
Opens React Email preview UI at http://localhost:3001:
- Live preview of all templates
- Mobile/desktop view toggle
- Built-in Tailwind CSS support
- Auto-reload on changes
2. Create or Edit Templates
Templates location: backend/emails/templates/
Shared Components:
_BaseLayout.tsx: Main email layout wrapper with Tailwind support_Button.tsx: Styled button component
Example Template:
import { Button, Html, Text, Tailwind } from '@react-email/components';
import { BaseLayout } from './_BaseLayout';
interface WelcomeEmailProps {
userName: string;
loginUrl: string;
}
export const WelcomeEmail = ({ userName, loginUrl }: WelcomeEmailProps) => (
<BaseLayout previewText="Welcome to Arive!">
<Text className="text-lg font-semibold text-foreground mb-4">
Hello {userName}!
</Text>
<Text className="text-muted-foreground mb-6">
Thanks for joining Arive. Get started by logging in:
</Text>
<Button href={loginUrl} className="bg-primary text-primary-foreground">
Log In Now
</Button>
</BaseLayout>
);
WelcomeEmail.PreviewProps = {
userName: "John Doe",
loginUrl: "https://app.tryarive.com/login",
} as WelcomeEmailProps;
export default WelcomeEmail;
3. Use Tailwind CSS Classes
The email templates support Tailwind CSS, which gets compiled to inline styles:
<div className="bg-neutral-50 p-4 rounded-lg border border-neutral-200">
<Text className="text-foreground font-semibold">Important Notice</Text>
<Text className="text-muted-foreground text-sm">Details here...</Text>
</div>
Available Theme Colors:
text-foreground/bg-foregroundtext-background/bg-backgroundtext-primary/bg-primarytext-muted/bg-mutedtext-muted-foreground- Full neutral gray scale:
neutral-50throughneutral-950
Config: backend/emails/tailwind.config.ts
4. Add Jinja2 Variables
Props in React components become Jinja2 template variables:
React:
interface MyEmailProps {
userName: string;
confirmUrl: string;
}
export const MyEmail = ({ userName, confirmUrl }: MyEmailProps) => (
<BaseLayout>
<Text>Hello {userName}</Text>
<Button href={confirmUrl}>Confirm</Button>
</BaseLayout>
);
Compiled HTML:
<p>Hello {{ userName }}</p>
<a href="{{ confirmUrl }}">Confirm</a>
5. Register Template Variables
Edit backend/emails/scripts/build.ts:
const TEMPLATE_VARIABLES: Record<string, Record<string, string>> = {
'WelcomeEmail': {
userName: 'John Doe',
loginUrl: 'https://app.tryarive.com/login',
},
'MyNewEmail': {
userName: 'Test User',
confirmUrl: 'https://app.tryarive.com/confirm/abc123',
},
};
6. Build Production Templates
make build-emails
This compiles:
- React Email components → HTML
- Tailwind CSS classes → inline styles
- Props → Jinja2 variables
- Output:
backend/templates/emails-react/*.html.jinja2
7. Use Template in Backend
Add method to EmailService in backend/app/lib/email.py:
async def send_welcome_email(
self,
to_email: str,
user_name: str,
login_url: str,
) -> None:
"""Send welcome email to new user."""
await self._send_email(
template_name="WelcomeEmail.html.jinja2",
to_email=to_email,
subject="Welcome to Arive!",
template_vars={
"userName": user_name,
"loginUrl": login_url,
},
)
Call from route handler:
@post("/register")
async def register_user(
data: RegisterRequest,
email_service: EmailService,
) -> UserResponse:
user = await create_user(data)
await email_service.send_welcome_email(
to_email=user.email,
user_name=user.name,
login_url=f"https://app.tryarive.com/login",
)
return user
Template Structure
backend/emails/
├── templates/ # React Email source (.tsx)
│ ├── _BaseLayout.tsx # Shared layout component
│ ├── _Button.tsx # Shared button component
│ ├── WelcomeEmail.tsx # Example template
│ └── MyEmail.tsx # Your new template
├── scripts/
│ ├── build.ts # Build script (React → HTML)
│ └── watch.ts # Dev server script
├── tailwind.config.ts # Tailwind CSS config
└── package.json # npm dependencies
backend/templates/emails-react/ # Compiled output (generated)
├── WelcomeEmail.html.jinja2
└── MyEmail.html.jinja2
Design System Guidelines
Colors
Use semantic color names that match the frontend:
- Foreground: Main text color (
text-foreground) - Muted: Secondary text (
text-muted-foreground) - Primary: Brand color for CTAs (
bg-primary) - Neutral grays: Backgrounds and borders (
bg-neutral-50,border-neutral-200)
Typography
<Text className="text-2xl font-bold text-foreground">Heading</Text>
<Text className="text-base text-foreground">Body text</Text>
<Text className="text-sm text-muted-foreground">Caption</Text>
Spacing
<div className="p-6"> {/* Padding */}
<div className="mb-4"> {/* Margin bottom */}
<div className="space-y-4"> {/* Vertical spacing between children */}
Layout
<Container className="max-w-xl"> {/* Max width container */}
<Section className="bg-neutral-50 rounded-lg p-6">
{/* Content */}
</Section>
</Container>
Testing Email Rendering
1. Preview in Dev Server
- Check desktop and mobile views
- Test all interactive elements
- Verify spacing and alignment
2. Send Test Emails
# In backend shell or test script
from app.lib.email import EmailService
email_service = EmailService()
await email_service.send_welcome_email(
to_email="your-email@example.com",
user_name="Test User",
login_url="https://app.tryarive.com/login",
)
3. Email Client Testing
Test in multiple email clients:
- Gmail (web + mobile app)
- Outlook (web + desktop)
- Apple Mail (macOS + iOS)
- Others as needed
Common Issues
Styles not applying
- Tailwind not compiling: Run
make build-emails - Inline styles missing: Check Tailwind config includes all classes
- Email client stripping styles: Use supported CSS properties only
Variables not rendering
- Jinja2 syntax errors: Check template output in
backend/templates/emails-react/ - Missing variables: Verify TEMPLATE_VARIABLES in
build.ts - Type mismatches: Ensure props match template variables
Images not loading
- Relative paths: Use absolute URLs for images
- CORS issues: Ensure image hosting allows email embedding
- Size: Optimize images for email (< 100KB recommended)
Best Practices
- Mobile-first: Design for mobile screens, test responsive behavior
- Inline styles: Tailwind automatically inlines, but verify after build
- Alt text: Always include alt text for images
- Preheader text: Set via
previewTextprop in BaseLayout - CTA buttons: Make them prominent and tap-friendly (44px min height)
- Dark mode: Consider email client dark mode support
- Accessibility: Use semantic HTML and proper color contrast
Production Deployment
Before deploying:
- Run
make build-emailsto compile templates - Commit compiled templates in
backend/templates/emails-react/ - Templates are included in Docker image build
- No Node.js runtime needed in production
Backend serves templates via Litestar's JinjaTemplateEngine at runtime.
Scan to join WeChat group