Building emails used to be the worst part of web development. You were stuck dealing with ancient HTML table structures, inline CSS, and testing across dozens of clients. If you wanted dynamic data, you had to inject strings into messy templates. It was fragile and painful.
That era is over.
In 2026, the standard for Next.js email architecture is React Email combined with Resend. You build emails as React components (just like your UI), and Resend handles the delivery infrastructure with a developer-first API [1]. This approach gives you type safety, component reusability, and modern CSS support right out of the box.
This guide covers how to set up a production-ready email system. We will not use nodemailer or raw HTML strings. We will build a system that scales from a simple "Welcome" email to complex transactional notifications.
Table of Contents
- The Architecture
- Step 1: Install and Configure
- Step 2: Build the Email Component
- Step 3: Create the Resend Client
- Step 4: Send Emails via Server Actions
- Step 5: Handle Production Domains
- Common Mistakes
The Architecture
The flow is simple but powerful. Instead of hitting an external API directly from the client (which exposes your keys), you route everything through your server.
User triggers action (Sign up, Purchase)
→ Server Action receives request
→ Database updates (Supabase/Postgres)
→ Server imports React Email component
→ Component renders to HTML with dynamic props
→ Resend API delivers the email
This ensures your Resend API Key never touches the browser [2]. It also allows you to centralize your email logic, making it easy to swap templates or update copy without touching your core business logic.
Step 1: Install and Configure
You need three packages to get started. The Resend SDK for sending, and the React Email packages for building templates [1].
npm install resend @react-email/components react-email
Next, add your API key to your environment variables. Do not prefix this with NEXT_PUBLIC_. It is a secret key.
# .env.local
RESEND_API_KEY=re_123456789...
You also need to configure a config object or similar constant to hold your "From" address and app details. This prevents hardcoding email addresses across your application.
Step 2: Build the Email Component
React Email allows you to build templates using standard React components like Html, Text, and Container. You place these in an emails/ directory.
Here is a real example of a "Purchase Delivery" email. Notice how it uses TypeScript interfaces for props. This ensures you never send an email with missing data.
// emails/purchase-delivery.tsx
import {
Body,
Container,
Head,
Heading,
Html,
Link,
Preview,
Text,
Hr,
} from '@react-email/components'
interface PurchaseDeliveryEmailProps {
name: string
productName: string
docsUrl: string
}
export function PurchaseDeliveryEmail({
name,
productName,
docsUrl,
}: PurchaseDeliveryEmailProps) {
return (
<Html>
<Head />
<Preview>Your purchase is ready!</Preview>
<Body style={main}>
<Container style={container}>
<Heading style={heading}>Thank you for your purchase!</Heading>
<Text style={paragraph}>Hi {name},</Text>
<Text style={paragraph}>
Your purchase of <strong>{productName}</strong> is confirmed.
</Text>
<Text style={paragraph}>
<strong>Read the documentation</strong>
<br />
<Link href={docsUrl} style={link}>
{docsUrl}
</Link>
</Text>
<Hr style={hr} />
<Text style={footer}>
— The Team
</Text>
</Container>
</Body>
</Html>
)
}
// Styles are defined as standard objects
const main = {
backgroundColor: '#ffffff',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
}
const container = {
margin: '0 auto',
padding: '40px 20px',
maxWidth: '560px',
}
const heading = {
fontSize: '24px',
fontWeight: '600' as const,
marginBottom: '24px',
}
const paragraph = {
fontSize: '16px',
lineHeight: '1.5',
marginBottom: '16px',
}
const link = {
color: '#0070f3',
textDecoration: 'underline' as const,
}
const hr = {
borderColor: '#e6e6e6',
margin: '24px 0',
}
const footer = {
fontSize: '14px',
color: '#666',
marginTop: '24px',
}
export default PurchaseDeliveryEmail
This component compiles down to the table-based HTML that email clients (like Outlook and Gmail) require [1]. You write modern code; the library handles the legacy mess.
Step 3: Create the Resend Client
Most tutorials tell you to export new Resend(process.env.RESEND_API_KEY) globally. This is dangerous.
If you initialize the client globally, your build might fail in CI/CD environments where the API key isn't present during the build step. In SecureStartKit, we use a lazy-initialization pattern to solve this.
// lib/resend/client.ts
import { Resend } from 'resend'
let _resend: Resend | null = null
export function getResend(): Resend {
if (!_resend) {
if (!process.env.RESEND_API_KEY) {
throw new Error('RESEND_API_KEY is not set')
}
_resend = new Resend(process.env.RESEND_API_KEY)
}
return _resend
}
This ensures the client is only created at runtime when you actually attempt to send an email, not when Next.js is statically analyzing your imports.
Step 4: Send Emails via Server Actions
Do not put email sending logic directly inside your UI components. Create a dedicated utility file that handles the actual sending. This allows you to call email functions from Server Actions, API routes, or background jobs [3].
Here is how to structure the sending logic:
// lib/resend/send.ts
import { getResend } from './client'
import { PurchaseDeliveryEmail } from '@/emails/purchase-delivery'
const FROM_EMAIL = 'Acme <onboarding@resend.dev>'
export async function sendPurchaseEmail(
to: string,
name: string,
productName: string
) {
const docsUrl = 'https://acme.com/docs'
await getResend().emails.send({
from: FROM_EMAIL,
to,
subject: 'Your purchase is ready',
react: PurchaseDeliveryEmail({
name,
productName,
docsUrl,
}),
})
}
You can now call this function from any Server Action.
// actions/checkout.ts
'use server'
import { sendPurchaseEmail } from '@/lib/resend/send'
import { z } from 'zod'
export async function processOrder(formData: FormData) {
// ... validate payment and save to DB ...
// Send the email
await sendPurchaseEmail(
'user@example.com',
'Alice',
'Pro Plan'
)
return { success: true }
}
Note that we pass the React component directly to the react property of the send method. Resend handles the rendering to HTML automatically [4]. If you are using an older workflow or need the raw HTML for logging, you can use the render function from @react-email/components explicitly.
Step 5: Handle Production Domains
When you are developing, you will use onboarding@resend.dev as your "From" address. This works for testing but will not work in production.
For production, you must verify your domain in the Resend dashboard [1].
- Add your domain (e.g.,
example.com). - Add the DNS records (TXT, MX, CNAME) provided by Resend to your DNS provider.
- Once verified, change your
FROM_EMAILconstant tonoreply@example.comorsupport@example.com.
If you try to send emails from an unverified domain in production, they will either bounce or go straight to spam [5].
Common Mistakes
Exposing API Keys. Never use the Resend SDK in a Client Component ('use client'). If you do, your API key will be visible in the network tab. Always wrap sending logic in Server Actions or API routes [2].
Ignoring Attachments. Sending large files as base64 strings can hit payload limits. Resend supports attachments via buffer or URL, but keep them small. For large files (like PDFs), generate a secure download link and include that in the email body instead of attaching the file directly [1].
Hardcoding URLs. Do not hardcode http://localhost:3000 in your templates. Use an environment variable like NEXT_PUBLIC_APP_URL so your links work correctly in both dev and production.
Missing use server. If you call your email function directly from a form, ensure the file or function is marked with 'use server'. Without this, Next.js treats it as client-side code, which leads to security issues and import errors [3].
By following this architecture, you decouple your email design from your application logic. Your templates stay clean, your keys stay secure, and your delivery rates stay high.
References
- Source from github.com— github.com
- Send Emails from Next.js with Resend and React Email - DEV Community— dev.to
- Next.js Send Email: Tutorial with Code Snippets [2026]— mailtrap.io
- Source from sequenzy.com— sequenzy.com
- Source from buildwithmatija.com— buildwithmatija.com
- Source from tiny.cloud— tiny.cloud
- EmailJS React: Tutorial with Code Snippets [2026]— mailtrap.io
Related Posts
How to Add Stripe Payments to Next.js Using Server Actions (2026 Guide)
A production-ready guide to integrating Stripe one-time payments in Next.js 15 with Server Actions, Zod validation, webhooks, and automated email delivery.
Supabase Authentication in Next.js App Router: The Complete 2026 Guide
A production-ready guide to implementing secure, server-side authentication with Supabase and Next.js App Router, moving beyond outdated client-side patterns.
Getting Started with SecureStartKit
Set up your SecureStartKit SaaS template in under 10 minutes. Clone, configure, and deploy.