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

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

Step-by-step guide to sending React Email templates with Mailgun 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 Mailgun in Next.js (2026 Guide)

Mailgun is a developer-focused email API with strong deliverability tools, detailed logging, and a generous free tier. It's a popular choice for startups that want more control over deliverability than larger platforms offer. This guide shows you exactly how to integrate React Email with Mailgun 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 Mailgun 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 mailgun.js form-data @react-email/render

@react-email/render converts your React Email component to an HTML string, which you then pass to Mailgun'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:

MAILGUN_API_KEY=key-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
MAILGUN_DOMAIN=mg.yourdomain.com
  • MAILGUN_API_KEYFound in the Mailgun dashboard under Settings → API Keys
  • MAILGUN_DOMAINYour verified Mailgun sending domain

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 Mailgun Client

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

import Mailgun from 'mailgun.js';
import FormData from 'form-data';
const mailgun = new Mailgun(FormData);
const mg = mailgun.client({
username: 'api',
key: process.env.MAILGUN_API_KEY!,
});

Step 5: Create the API Route

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

import { render } from '@react-email/render';
import Mailgun from 'mailgun.js';
import FormData from 'form-data';
import { WelcomeEmail } from '../../../emails/welcome-email';
const mailgun = new Mailgun(FormData);
const mg = mailgun.client({
username: 'api',
key: process.env.MAILGUN_API_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 result = await mg.messages.create(process.env.MAILGUN_DOMAIN!, {
from: 'Your App <hello@yourdomain.com>',
to: [email],
subject: `Welcome, ${username}!`,
html,
});
return Response.json({ id: result.id });
}

Pages Router (pages/api/send.ts)

import type { NextApiRequest, NextApiResponse } from 'next';
import { render } from '@react-email/render';
import Mailgun from 'mailgun.js';
import FormData from 'form-data';
import { WelcomeEmail } from '../../emails/welcome-email';
const mailgun = new Mailgun(FormData);
const mg = mailgun.client({
username: 'api',
key: process.env.MAILGUN_API_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 result = await mg.messages.create(process.env.MAILGUN_DOMAIN!, {
from: 'Your App <hello@yourdomain.com>',
to: [email],
subject: `Welcome, ${username}!`,
html,
});
return res.status(200).json({ id: result.id });
}

Mailgun-Specific Tips

  • Mailgun uses EU and US regions. If your account is on the EU region, initialize the client with `url: 'https://api.eu.mailgun.net'`.
  • New Mailgun accounts are in sandbox mode and can only send to authorized recipients. Add authorized recipients in the dashboard or upgrade to send freely.
  • The `form-data` package is a required peer dependency of `mailgun.js` — it must be installed separately.
  • Mailgun supports email tracking (opens, clicks) and webhooks for bounce/complaint events — configure these in the dashboard for better deliverability management.

Common Errors and Fixes

ErrorCauseFix
Unauthorized (401)Invalid API keyCheck MAILGUN_API_KEY starts with "key-" and is the Private API Key, not the Public Validation Key
Domain not found (404)Wrong MAILGUN_DOMAIN valueUse your exact sending domain from the Mailgun Sending → Domains list, e.g. mg.yourdomain.com
Cannot read property 'create' of undefinedform-data not installedRun npm install form-data — it is required by mailgun.js

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 Mailgun.

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