Once you’ve created your email template, you need to render it to HTML and send it through an email service. React Email’s render function converts your React components to email-safe HTML.
Rendering templates
The @react-email/render package converts React components to HTML strings:
import { render } from "@react-email/render" ;
import WelcomeEmail from "./emails/welcome" ;
const html = await render ( < WelcomeEmail userName = "John" /> );
// Returns: HTML string ready to send
The render function automatically converts Tailwind classes to inline styles and ensures the HTML works across all email clients.
Sending with Plunk
ReactUI Email uses Plunk for sending emails. Plunk is a modern email API designed for developers.
Setup
Get your API key
Sign up at useplunk.com and get your API key from the dashboard.
Set environment variable
PLUNK_SECRET_KEY = your_api_key_here
Sending emails
Here’s the implementation from ReactUI Email:
src/features/email/send/email-trigger.ts
import Plunk from "@plunk/node" ;
import { render } from "@react-email/render" ;
import { JSXElementConstructor , ReactElement } from "react" ;
const plunk = new Plunk ( process . env . PLUNK_SECRET_KEY ! );
export const emailTrigger = async ({
template ,
subject ,
to ,
} : {
template : ReactElement < unknown , string | JSXElementConstructor < unknown >>;
subject : string ;
to : string ;
}) => {
try {
const body = await render ( template );
const response = await plunk . emails . send ({
to: to ,
subject ,
body ,
});
return response . success ;
} catch ( error ) {
console . error ( error );
}
};
Usage example
import { emailTrigger } from "./email-trigger" ;
import WelcomeEmail from "./emails/welcome" ;
await emailTrigger ({
template: < WelcomeEmail userName = "John Doe" /> ,
subject: "Welcome to our platform!" ,
to: "john@example.com" ,
});
Sending with Resend
Resend is a popular email API built for developers.
Setup
Create the send function
import { Resend } from "resend" ;
import { render } from "@react-email/render" ;
const resend = new Resend ( process . env . RESEND_API_KEY );
export async function sendEmail ({
to ,
subject ,
template ,
} : {
to : string ;
subject : string ;
template : React . ReactElement ;
}) {
const html = await render ( template );
const { data , error } = await resend . emails . send ({
from: "noreply@yourdomain.com" ,
to ,
subject ,
html ,
});
if ( error ) {
throw new Error ( error . message );
}
return data ;
}
Usage
import { sendEmail } from "./lib/email" ;
import WelcomeEmail from "./emails/welcome" ;
await sendEmail ({
to: "user@example.com" ,
subject: "Welcome aboard!" ,
template: < WelcomeEmail userName = "Sarah" /> ,
});
Resend provides a generous free tier and excellent TypeScript support.
Sending with SendGrid
SendGrid is a widely-used email delivery platform.
Setup
Install SendGrid SDK
npm install @sendgrid/mail
Configure SendGrid
import sgMail from "@sendgrid/mail" ;
import { render } from "@react-email/render" ;
sgMail . setApiKey ( process . env . SENDGRID_API_KEY ! );
export async function sendEmail ({
to ,
subject ,
template ,
} : {
to : string ;
subject : string ;
template : React . ReactElement ;
}) {
const html = await render ( template );
const msg = {
to ,
from: "noreply@yourdomain.com" ,
subject ,
html ,
};
try {
await sgMail . send ( msg );
return { success: true };
} catch ( error ) {
console . error ( error );
throw error ;
}
}
Sending with Nodemailer
Nodemailer is a popular Node.js module for sending emails via SMTP.
Setup
Install Nodemailer
npm install nodemailer
npm install -D @types/nodemailer
Configure transporter
import nodemailer from "nodemailer" ;
import { render } from "@react-email/render" ;
// Create reusable transporter
const transporter = nodemailer . createTransport ({
host: process . env . SMTP_HOST ,
port: parseInt ( process . env . SMTP_PORT || "587" ),
secure: false ,
auth: {
user: process . env . SMTP_USER ,
pass: process . env . SMTP_PASSWORD ,
},
});
export async function sendEmail ({
to ,
subject ,
template ,
} : {
to : string ;
subject : string ;
template : React . ReactElement ;
}) {
const html = await render ( template );
const info = await transporter . sendMail ({
from: '"Your Company" <noreply@yourcompany.com>' ,
to ,
subject ,
html ,
});
return info ;
}
Using with Gmail
const transporter = nodemailer . createTransport ({
service: "gmail" ,
auth: {
user: process . env . GMAIL_USER ,
pass: process . env . GMAIL_APP_PASSWORD , // Use app-specific password
},
});
Don’t use your regular Gmail password. Create an App Password instead.
Sending in Next.js
API Route
Create an API route to send emails:
app/api/send-email/route.ts
import { NextResponse } from "next/server" ;
import { render } from "@react-email/render" ;
import { Resend } from "resend" ;
import WelcomeEmail from "@/emails/welcome" ;
const resend = new Resend ( process . env . RESEND_API_KEY );
export async function POST ( request : Request ) {
try {
const { to , userName } = await request . json ();
const html = await render ( < WelcomeEmail userName = { userName } /> );
const { data , error } = await resend . emails . send ({
from: "noreply@yourdomain.com" ,
to ,
subject: "Welcome!" ,
html ,
});
if ( error ) {
return NextResponse . json ({ error }, { status: 400 });
}
return NextResponse . json ({ success: true , data });
} catch ( error ) {
return NextResponse . json ({ error: "Failed to send email" }, { status: 500 });
}
}
Server Actions
app/actions/send-email.ts
"use server" ;
import { render } from "@react-email/render" ;
import { Resend } from "resend" ;
import WelcomeEmail from "@/emails/welcome" ;
const resend = new Resend ( process . env . RESEND_API_KEY );
export async function sendWelcomeEmail ( to : string , userName : string ) {
const html = await render ( < WelcomeEmail userName = { userName } /> );
const { data , error } = await resend . emails . send ({
from: "noreply@yourdomain.com" ,
to ,
subject: "Welcome!" ,
html ,
});
if ( error ) {
throw new Error ( error . message );
}
return data ;
}
Testing emails
Preview in development
React Email includes a development server for previewing emails:
This opens a browser window where you can view and test your templates.
Send test emails
Use a test email service during development:
Free fake SMTP service for testing: import nodemailer from "nodemailer" ;
// Create test account
const testAccount = await nodemailer . createTestAccount ();
const transporter = nodemailer . createTransport ({
host: "smtp.ethereal.email" ,
port: 587 ,
secure: false ,
auth: {
user: testAccount . user ,
pass: testAccount . pass ,
},
});
const info = await transporter . sendMail ({
from: '"Test" <test@example.com>' ,
to: "user@example.com" ,
subject: "Test Email" ,
html: await render ( < WelcomeEmail /> ),
});
console . log ( "Preview URL: %s" , nodemailer . getTestMessageUrl ( info ));
Mailtrap provides a safe email testing environment:const transporter = nodemailer . createTransport ({
host: "smtp.mailtrap.io" ,
port: 2525 ,
auth: {
user: process . env . MAILTRAP_USER ,
pass: process . env . MAILTRAP_PASS ,
},
});
Error handling
Always handle errors when sending emails:
export async function sendEmail ({
to ,
subject ,
template ,
} : {
to : string ;
subject : string ;
template : React . ReactElement ;
}) {
try {
const html = await render ( template );
const { data , error } = await resend . emails . send ({
from: "noreply@yourdomain.com" ,
to ,
subject ,
html ,
});
if ( error ) {
console . error ( "Email sending failed:" , error );
throw new Error ( error . message );
}
return { success: true , data };
} catch ( error ) {
console . error ( "Unexpected error:" , error );
return { success: false , error: "Failed to send email" };
}
}
Best practices
Use environment variables Never hardcode API keys. Always use environment variables for sensitive credentials.
Validate email addresses Validate email addresses before sending to avoid bounces and errors.
Handle failures gracefully Implement retry logic and proper error handling for failed sends.
Monitor delivery Use your email service’s dashboard to monitor delivery rates and engagement.
Most email services provide webhooks for tracking delivery, opens, and clicks. Implement these to improve your email strategy.
Rate limiting
Implement rate limiting to avoid hitting API limits:
import { Ratelimit } from "@upstash/ratelimit" ;
import { Redis } from "@upstash/redis" ;
const ratelimit = new Ratelimit ({
redis: Redis . fromEnv (),
limiter: Ratelimit . slidingWindow ( 10 , "1 m" ), // 10 emails per minute
});
export async function sendEmail ({ to , subject , template } : EmailParams ) {
const { success } = await ratelimit . limit ( to );
if ( ! success ) {
throw new Error ( "Rate limit exceeded" );
}
// Send email...
}
Next steps
React Email basics Learn more about React Email components
Customization Customize templates for your brand