The Modern Email Builder for SaaS Products
The modern email builder for SaaS products is a development workflow that uses React components to define transactional email templates, renders them server-side to HTML for delivery, and sends them through a transactional email service with strong deliverability. This approach replaces the traditional pattern of HTML string manipulation, MJML preprocessing, or visual email builders with a developer-native workflow where email templates are version-controlled, typed, and testable in the same environment as the rest of the application.
Written by Yashveer Singh, founder of Yashveer Labs.
What you actually need to know
- React Email components render to email-compatible HTML, handling the cross-client CSS compatibility issues that make raw HTML email development painful.
- Resend + React Email is the lowest-friction transactional email stack for SaaS:
emails.send({ react: <WelcomeEmail name={user.name} /> })is the entire sending call. npx email devstarts a local preview server. See the email template in the browser before sending to any real address.- SPF, DKIM, and DMARC DNS records are required for deliverability. Without them, custom-domain email goes to spam. Configure all three before sending production email.
- Email templates belong in version control as
.tsxfiles, not in a marketing tool's visual editor -- unless the marketing team owns and edits the templates independently.
| Email Client | CSS Support | Key Limitations |
|---|---|---|
| Gmail (Web) | Limited CSS | Strips <style> blocks |
| Gmail (Mobile) | Moderate | Some media queries supported |
| Outlook (Windows) | Poor | Uses Word rendering engine |
| Apple Mail | Good | Supports most CSS |
| Outlook.com | Moderate | Strips some CSS properties |
The core argument
Transactional email templates in most codebases exist as one of three things: string interpolation with HTML embedded in code, MJML files that require a separate build step, or templates managed in a marketing tool's visual builder that are outside version control. All three approaches have the same problem: they are hard to test, hard to maintain, and prone to cross-client rendering issues that are only discovered when a real user reports that the email looks broken in Outlook.
React Email takes a different approach: email templates are React components that use a set of components designed for email-compatible HTML output. The developer writes what looks like a normal React component, tests it in a browser preview environment, and gets HTML that works in Gmail, Outlook, and Apple Mail without manually managing the inline style requirements that email clients impose.
The value is not just the development experience. It is the maintainability: the email template is a .tsx file in the repository, typed with the same TypeScript the rest of the application uses, reviewable in pull requests, and testable in isolation. When the brand color changes or the footer needs a new legal disclaimer, the change is made in one place and applies to all templates that use the shared layout component.
The React Email component structure
A welcome email for a SaaS product:
```tsx // emails/welcome.tsx import { Html, Head, Body, Container, Text, Heading, Button, Link, Hr, Img } from '@react-email/components'; import { render } from '@react-email/render';
interface WelcomeEmailProps { userName: string; dashboardUrl: string; supportEmail: string; }
export function WelcomeEmail({ userName, dashboardUrl, supportEmail, }: WelcomeEmailProps) { return ( <Html lang="en"> <Head /> <Body style={bodyStyle}> <Container style={containerStyle}> <Img src="https://yashveerlabs.com/logo.png" width={120} height={40} alt="Yashveer Labs" /> <Heading style={headingStyle}> Welcome, {userName} </Heading> <Text style={textStyle}> Your account is set up and ready. Here is what to do next. </Text> <Button href={dashboardUrl} style={buttonStyle}> Open your dashboard </Button> <Hr style={hrStyle} /> <Text style={footerStyle}> Questions? Reply to this email or{' '} <Link href={mailto:${supportEmail}}> contact support </Link> . </Text> </Container> </Body> </Html> ); }
// Styles are inline objects -- email clients require inline styles const bodyStyle = { backgroundColor: '#f9f9f9', fontFamily: 'sans-serif' }; const containerStyle = { maxWidth: '600px', margin: '0 auto', padding: '24px' }; const headingStyle = { fontSize: '24px', color: '#111' }; const textStyle = { fontSize: '16px', color: '#444', lineHeight: '1.5' }; const buttonStyle = { backgroundColor: '#0070f3', color: '#fff', padding: '12px 24px', borderRadius: '4px', textDecoration: 'none', display: 'inline-block', }; const hrStyle = { borderColor: '#eee', margin: '24px 0' }; const footerStyle = { fontSize: '14px', color: '#888' }; ```
The important patterns: all styles are inline objects (not CSS classes -- many email clients strip <style> blocks and class-based styles), images use absolute URLs (relative URLs do not resolve in email clients), and the component accepts typed props (the caller must provide all required data, which prevents sending an email with missing content).
Sending with Resend
```typescript // lib/email.ts import { Resend } from 'resend'; import { WelcomeEmail } from '../emails/welcome';
const resend = new Resend(process.env.RESEND_API_KEY);
export async function sendWelcomeEmail(user: { name: string; email: string; dashboardUrl: string; }) { const result = await resend.emails.send({ from: 'Yashveer Labs <hello@yashveerlabs.com>', to: user.email, subject: Welcome to Yashveer Labs, ${user.name}, react: WelcomeEmail({ userName: user.name, dashboardUrl: user.dashboardUrl, supportEmail: 'support@yashveerlabs.com', }), });
if (result.error) { throw new Error(Failed to send welcome email: ${result.error.message}); }
return result.data; } ```
The react property accepts the React element directly -- Resend's SDK handles the server-side rendering to HTML. No render() call required; no HTML string management.
Shared layout components
Shared header, footer, and standard text styles belong in shared components:
```tsx // emails/components/EmailLayout.tsx import { Html, Head, Body, Container, Img, Hr, Text } from '@react-email/components';
export function EmailLayout({ children }: { children: React.ReactNode }) { return ( <Html lang="en"> <Head /> <Body style={{ backgroundColor: '#f9f9f9', fontFamily: 'sans-serif' }}> <Container style={{ maxWidth: '600px', margin: '0 auto', padding: '24px' }}> <Img src="https://yashveerlabs.com/logo.png" width={120} height={40} alt="Yashveer Labs" style={{ marginBottom: '24px' }} /> {children} <Hr style={{ borderColor: '#eee', margin: '24px 0' }} /> <Text style={{ fontSize: '12px', color: '#aaa', textAlign: 'center' }}> Yashveer Labs · 123 Example St · support@yashveerlabs.com </Text> </Container> </Body> </Html> ); } ```
Every email template uses EmailLayout as its wrapper. When the logo, footer, or brand colors change, the change is made once in EmailLayout and applies to all templates.
Deliverability configuration
Email deliverability depends on DNS records that authorize the sending domain. Configure all three before sending production email:
SPF (Sender Policy Framework). A TXT record on the sending domain that lists the IP addresses and services authorized to send email on the domain's behalf.
For Resend: add a TXT record to the domain with Resend's SPF include value (provided in Resend's DNS setup guide).
DKIM (DomainKeys Identified Mail). A DNS record that contains the public key used to verify Resend's cryptographic signature on each email. Configured by adding a CNAME record that Resend provides.
DMARC (Domain-based Message Authentication, Reporting, and Conformance). A TXT record that tells receiving email servers what to do when SPF or DKIM fails. Start with a reporting-only policy:
`` v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com ``
After monitoring for false positives (legitimate email being flagged), upgrade to a quarantine or reject policy.
All three records are configured in the domain's DNS settings (Cloudflare, Namecheap, or wherever the domain's DNS is managed). Resend's onboarding wizard provides the exact record values and verification.
Common mistakes teams make with transactional email
- Not testing templates across real email clients. An email that renders correctly in Chrome does not necessarily render correctly in Outlook -- Outlook uses the Word rendering engine, which has poor CSS support. Use Litmus or Email on Acid for cross-client testing before sending to real users.
- Using relative image URLs in email templates. Relative URLs do not resolve in email clients. All image URLs must be absolute (https://yourdomain.com/images/logo.png).
- Not configuring DKIM and SPF before sending production email. Email sent without DKIM/SPF from a custom domain lands in spam at significantly higher rates. Configure DNS records before sending the first production email, not after receiving user complaints.
- Not handling email bounce and unsubscribe webhooks. Resend and other providers send webhooks for bounced emails and unsubscribes. Not handling these means continuing to send email to invalid or unsubscribed addresses, which damages deliverability scores.
- Hardcoding email content instead of using props. Email templates with hardcoded user names or URLs are untestable and unmaintainable. All dynamic content should be a prop that the caller provides.
Where to start: a 3-step transactional email setup
Step 1: Install React Email and create the first template with `npx email dev`. The local preview confirms the template renders correctly before any sending configuration. Start with the welcome email -- it is the highest-value transactional email and a good template for establishing the pattern.
Step 2: Configure Resend, add DNS records for SPF/DKIM/DMARC, and verify the sending domain. This takes 30-60 minutes and must be done before sending any production email. Test with Resend's test mode to verify delivery to a real inbox.
Step 3: Create the shared EmailLayout component and refactor the first template to use it. This establishes the pattern that all future templates will follow. Shared layout components make the template library maintainable as the number of email types grows.
The Email That Looks Right in Every Client
Yashveer Singh. Founder of Yashveer Labs. React Email replaced the HTML string templates on Expert Tutorials that had been causing rendering issues in Outlook for months. The old templates used CSS classes that Outlook stripped; the headings and buttons rendered without styling. Migrating to React Email's components, which use inline styles by default, fixed the Outlook rendering in one afternoon. The migration also produced typed templates -- instead of string interpolation that could silently produce empty values, the TypeScript compiler enforces that every template receives all required props. The email that reaches the user looks correct because the template was designed in a preview environment that shows exactly what the email client will render.
Related reading
Frequently asked
About me and why that should matter to you
Yashveer Singh. Full stack developer. Founder of Yashveer Labs. Based in New Delhi. The reason it should matter to you is that most engineers writing about this topic have not actually done it. I have. The code is on GitHub. The systems are on real URLs. The portfolio has the proof. The contact channel is Instagram. If the work needs to get done, that is how you reach me.
Posts that line up with this one.
- Web App and Frontend Development
The Modern Web App Stack: A 2026 Survey
The full-stack choices that are stable, well-supported, and worth learning in 2026 -- from framework to database to deployment.
- Web App and Frontend Development
Loading States, Skeletons, and Optimistic UI
How you handle loading states is one of the most visible indicators of product quality. Here is the decision framework for when to use spinners, skeletons, and optimistic updates, and the common mistakes that make apps feel slow.
- Web App and Frontend Development
Modal Patterns That Do Not Trap Users
Modals are overused, frequently misimplemented, and a common source of user frustration. Here is how to design and build modals that provide the right information at the right time without trapping users or creating accessibility failures.
- Web App and Frontend Development
Next.js vs Remix vs Astro vs Nuxt in 2026
Next.js, Remix, Astro, and Nuxt each make different architectural bets about how web applications should work. Here is how they compare in 2026 and which one belongs in which project.