Postmark is a transactional email service focused on fast, reliable delivery. It has a straightforward API and excellent deliverability for password resets, receipts, and notifications. This guide shows you exactly how to integrate React Email with Postmark 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 Postmark 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 postmark @react-email/render@react-email/render converts your React Email component to an HTML string, which you then pass to Postmark'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:
POSTMARK_SERVER_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxPOSTMARK_SERVER_TOKEN— Found in your Postmark server settings under "API Tokens"
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.tsximport * 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 emailsOpen http://localhost:3000 to see your templates rendered exactly as they will appear in email clients.
Step 4: Initialize the Postmark Client
Create the client once at module level so it's reused across requests:
import { ServerClient } from 'postmark';
const client = new ServerClient(process.env.POSTMARK_SERVER_TOKEN!);Step 5: Create the API Route
App Router (app/api/send/route.ts)
import { render } from '@react-email/render';import { ServerClient } from 'postmark';import { WelcomeEmail } from '../../../emails/welcome-email';
const client = new ServerClient(process.env.POSTMARK_SERVER_TOKEN!);
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 client.sendEmail({ From: 'hello@yourdomain.com', To: email, Subject: `Welcome, ${username}!`, HtmlBody: html, MessageStream: 'outbound', });
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 { ServerClient } from 'postmark';import { WelcomeEmail } from '../../emails/welcome-email';
const client = new ServerClient(process.env.POSTMARK_SERVER_TOKEN!);
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 client.sendEmail({ From: 'hello@yourdomain.com', To: email, Subject: `Welcome, ${username}!`, HtmlBody: html, MessageStream: 'outbound', });
return res.status(200).json({ messageId: result.MessageID });}Postmark-Specific Tips
- You must specify `MessageStream: "outbound"` for transactional emails. Postmark separates transactional and broadcast streams.
- Your `From` address must be a verified sender signature in the Postmark dashboard.
- Postmark has a sandbox mode — use the test API token (`POSTMARK_API_TEST`) during development to process emails without delivering them.
- Postmark supports `TextBody` alongside `HtmlBody`. Use `render(component, { plainText: true })` from `@react-email/render` to generate the plain-text version.
Common Errors and Fixes
| Error | Cause | Fix |
|---|---|---|
Invalid API token | Wrong token or using account token instead of server token | Use the Server API Token from your specific Postmark server, not the account-level token |
Sender signature not found | The From address is not a verified sender in Postmark | Add and verify your From address under Sender Signatures in the Postmark dashboard |
Message stream not found | Missing or wrong MessageStream value | Set MessageStream to "outbound" for transactional emails |
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.tsxEach template uses the same @react-email/components primitives and is rendered to HTML with @react-email/render before being passed to Postmark.
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.
