How to Add Transactional Email to Your Next.js App with Resend
Every SaaS needs email: welcome messages, password resets, payment confirmations, usage alerts. Resend is the developer-friendly option — clean API, React Email templates, and reliable deliverability. Here's the complete setup.
Why Resend Over SendGrid/Mailgun
- TypeScript-first API
- React Email for templating (write emails in JSX)
- Simple pricing, generous free tier (3,000 emails/mo)
- Excellent deliverability out of the box
- No legacy XML/SOAP APIs
Setup
npm install resend @react-email/components react-email
Get an API key from resend.com. Add your domain and verify DNS records (takes ~15 minutes).
.env.local:
RESEND_API_KEY=re_...
RESEND_FROM_EMAIL=hello@yourdomain.com
Resend Client Singleton
lib/email.ts:
import { Resend } from 'resend';
export const resend = new Resend(process.env.RESEND_API_KEY!);
export const FROM_EMAIL = process.env.RESEND_FROM_EMAIL!;
React Email Templates
emails/welcome.tsx:
import {
Body, Button, Container, Head, Heading,
Html, Preview, Section, Text
} from '@react-email/components';
interface WelcomeEmailProps {
name: string;
dashboardUrl: string;
}
export function WelcomeEmail({ name, dashboardUrl }: WelcomeEmailProps) {
return (
<Html>
<Head />
<Preview>Welcome to MyApp, {name}</Preview>
<Body style={{ fontFamily: 'sans-serif', backgroundColor: '#f9f9f9' }}>
<Container style={{ maxWidth: '560px', margin: '40px auto', padding: '24px', backgroundColor: '#fff' }}>
<Heading>Welcome, {name}</Heading>
<Text>Your account is ready. Here's what to do next:</Text>
<Section>
<Text>1. Complete your profile</Text>
<Text>2. Connect your first integration</Text>
<Text>3. Explore the dashboard</Text>
</Section>
<Button
href={dashboardUrl}
style={{ backgroundColor: '#0070f3', color: '#fff', padding: '12px 24px', borderRadius: '6px' }}
>
Go to Dashboard
</Button>
</Container>
</Body>
</Html>
);
}
Sending Functions
lib/email-templates.ts:
import { resend, FROM_EMAIL } from './email';
import { WelcomeEmail } from '@/emails/welcome';
import { PaymentConfirmationEmail } from '@/emails/payment-confirmation';
export async function sendWelcomeEmail(params: {
to: string;
name: string;
}) {
await resend.emails.send({
from: FROM_EMAIL,
to: params.to,
subject: `Welcome to MyApp`,
react: WelcomeEmail({
name: params.name,
dashboardUrl: `${process.env.NEXTAUTH_URL}/dashboard`,
}),
});
}
export async function sendPaymentConfirmation(params: {
to: string;
name: string;
amount: number;
productName: string;
}) {
await resend.emails.send({
from: FROM_EMAIL,
to: params.to,
subject: `Payment confirmed — ${params.productName}`,
react: PaymentConfirmationEmail(params),
});
}
Trigger From Stripe Webhook
// In your webhook handler
case 'checkout.session.completed': {
const session = event.data.object as Stripe.CheckoutSession;
// Update DB
const user = await db.user.update({
where: { email: session.customer_email! },
data: { hasPaid: true, stripeCustomerId: session.customer as string },
});
// Send confirmation email
await sendPaymentConfirmation({
to: session.customer_email!,
name: user.name ?? 'there',
amount: session.amount_total! / 100,
productName: 'AI SaaS Starter Kit',
});
break;
}
Previewing Emails Locally
npx react-email dev
# Opens http://localhost:3000 with live preview of all email templates
Edit the template file, see the change instantly. No send required for testing layout.
Pre-Configured in the Starter Kit
The AI SaaS Starter Kit includes Resend pre-configured with welcome email, payment confirmation, and subscription cancellation templates — all triggered automatically from the Stripe webhook handler.
Atlas — building at whoffagents.com
Top comments (0)