Launch Special: Use code LAUNCH20 for $20 off — Just $59 $79
Logo
Back

Send React Email with AWS SES in Next.js (2026 Guide)

Step-by-step guide to sending React Email templates with AWS SES in Next.js. Covers install, env setup, App Router & Pages Router examples, error handling, and tips.

Guides
Soufiane E.
Soufiane E.
8 min read
Send React Email with AWS SES in Next.js (2026 Guide)

Amazon Simple Email Service (SES) is the lowest-cost option for high-volume email and integrates natively with the rest of your AWS infrastructure. It requires more setup than developer-focused providers but scales to billions of emails. This guide shows you exactly how to integrate React Email with AWS SES in a Next.js project — covering environment setup, rendering templates to HTML, sending with both App Router and Pages Router, and handling common errors.


Prerequisites

Before starting, you need:

  • A AWS SES account with API credentials
  • A Next.js project (App Router or Pages Router — both covered below)
  • Node.js 18+

Step 1: Install Dependencies

npm install @aws-sdk/client-ses @react-email/render

@react-email/render converts your React Email component to an HTML string, which you then pass to AWS SES's API. Unlike Resend (which accepts React elements directly), most providers require rendered HTML.


Step 2: Configure Environment Variables

Add the following to your .env.local file:

AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
AWS_REGION=us-east-1
  • AWS_ACCESS_KEY_IDIAM user access key with SES send permissions
  • AWS_SECRET_ACCESS_KEYIAM user secret key
  • AWS_REGIONThe AWS region where your SES is configured

When deploying, add these variables in your hosting provider's environment settings (Vercel dashboard, Railway, Fly.io, etc.). Variables in .env.local do not deploy automatically.


Step 3: Create a React Email Template

Create an emails/ directory and add your first template. All providers use the same React Email template format — the difference is only in how you send it.

// emails/welcome-email.tsx
import * as React from 'react';
import {
Body, Button, Container, Head,
Heading, Html, Preview, Section, Text,
} from '@react-email/components';
interface WelcomeEmailProps {
username: string;
dashboardUrl: string;
}
export function WelcomeEmail({ username, dashboardUrl }: WelcomeEmailProps) {
return (
<Html>
<Head />
<Preview>Your account is ready — let's get started</Preview>
<Body style={{ fontFamily: 'sans-serif', backgroundColor: '#f4f4f5' }}>
<Container style={{ margin: '0 auto', padding: '40px 20px', maxWidth: '560px' }}>
<Heading>Welcome, {username}!</Heading>
<Text>Your account is set up and ready to use.</Text>
<Section>
<Button
href={dashboardUrl}
style={{ backgroundColor: '#000', color: '#fff', padding: '12px 24px', borderRadius: '6px' }}
>
Go to Dashboard
</Button>
</Section>
</Container>
</Body>
</Html>
);
}
WelcomeEmail.PreviewProps = {
username: 'Alex',
dashboardUrl: 'https://example.com/dashboard',
} as WelcomeEmailProps;
export default WelcomeEmail;

Preview locally

Before sending real emails, use the React Email dev server to preview your templates:

npx react-email dev --dir emails

Open http://localhost:3000 to see your templates rendered exactly as they will appear in email clients.



Step 4: Initialize the AWS SES Client

Create the client once at module level so it's reused across requests:

import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';
const ses = new SESClient({
region: process.env.AWS_REGION!,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
});

Step 5: Create the API Route

App Router (app/api/send/route.ts)

import { render } from '@react-email/render';
import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';
import { WelcomeEmail } from '../../../emails/welcome-email';
const ses = new SESClient({
region: process.env.AWS_REGION!,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
});
export async function POST(request: Request) {
const { username, email } = await request.json();
const html = await render(
<WelcomeEmail username={username} dashboardUrl="https://yourdomain.com/dashboard" />
);
const command = new SendEmailCommand({
Source: 'hello@yourdomain.com',
Destination: { ToAddresses: [email] },
Message: {
Subject: { Data: `Welcome, ${username}!` },
Body: { Html: { Data: html, Charset: 'UTF-8' } },
},
});
const result = await ses.send(command);
return Response.json({ messageId: result.MessageId });
}

Pages Router (pages/api/send.ts)

import type { NextApiRequest, NextApiResponse } from 'next';
import { render } from '@react-email/render';
import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';
import { WelcomeEmail } from '../../emails/welcome-email';
const ses = new SESClient({
region: process.env.AWS_REGION!,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
});
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') return res.status(405).end();
const { username, email } = req.body;
const html = await render(
<WelcomeEmail username={username} dashboardUrl="https://yourdomain.com/dashboard" />
);
const command = new SendEmailCommand({
Source: 'hello@yourdomain.com',
Destination: { ToAddresses: [email] },
Message: {
Subject: { Data: `Welcome, ${username}!` },
Body: { Html: { Data: html, Charset: 'UTF-8' } },
},
});
const result = await ses.send(command);
return res.status(200).json({ messageId: result.MessageId });
}

AWS SES-Specific Tips

  • New AWS SES accounts start in sandbox mode — you can only send to verified email addresses. Request production access in the SES console to lift this restriction.
  • On AWS infrastructure (EC2, Lambda, ECS), you can use IAM roles instead of access keys — omit the `credentials` config and the SDK picks up the role automatically.
  • Use `SESv2Client` and `SendEmailCommand` from `@aws-sdk/client-sesv2` if you need features like templates, configuration sets, or contact lists.
  • SES is significantly cheaper than other providers at scale ($0.10 per 1,000 emails), but requires more ops work — you manage your own bounce and complaint handling.

Common Errors and Fixes

ErrorCauseFix
Email address not verifiedIn sandbox mode, both From and To must be verifiedVerify your email addresses in the SES console, or request production access to remove this restriction
SignatureDoesNotMatchIncorrect AWS credentialsDouble-check AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are correct and not rotated
AccessDeniedIAM user lacks SES send permissionsAttach the `AmazonSESFullAccess` policy or a custom policy with `ses:SendEmail` to your IAM user

Organizing Multiple Email Templates

A SaaS app typically needs 8–12 email templates. Keep them organized in a dedicated directory:

emails/
├── welcome-email.tsx
├── otp-email.tsx
├── magic-link.tsx
├── password-reset.tsx
├── payment-receipt.tsx
├── trial-started.tsx
├── trial-ending.tsx
└── security-alert.tsx

Each template uses the same @react-email/components primitives and is rendered to HTML with @react-email/render before being passed to AWS SES.

If you don't want to write and cross-client test all these templates from scratch, the React Email Templates bundle includes 12 production-ready templates for the most common SaaS email flows — tested on Gmail, Outlook, Apple Mail, and mobile clients in both light and dark mode.


Related Guides