Send email from Next.js with emailr_
Send transactional email from Next.js App Router route handlers and server actions with the emailr_ SDK, and verify delivery webhooks safely.
The emailr SDK is a small, fetch-based TypeScript client, so it fits naturally into Next.js route handlers and server actions. This guide sets up sending from the App Router and receiving signed delivery events.
Keep the API key on the server: never import the client into a Client Component or expose the key with a NEXT_PUBLIC_ prefix.
Set up emailr_ with Next.js
- 01
Create an API key
Create a key under API keys in the dashboard. Keys start with
et_live_and are scoped to one domain workspace. Store it asEMAILR_API_KEYin your secret manager or environment, never in client-side code. - 02
Add and verify your sending domain
In the dashboard, open Domains and add the domain you send from. A subdomain such as
mail.yourcompany.comkeeps transactional reputation separate from your corporate mailbox.Emailr generates the DNS records: a DKIM TXT record on the
emailr._domainkeyselector, an SPF TXT record, and an MX + TXT pair on thesendsubdomain used as the MAIL FROM / Return-Path. A DMARC record is optional but recommended.Emailr's DKIM selector and
sendsubdomain don't collide with other providers' records, so you can add them before cutting over and send from both providers during the migration. A domain can only have one SPF record: if one already exists, merge Emailr'sinclude:into it instead of adding a second record. - 03
Install the SDK and add your key
Install the package and add
EMAILR_API_KEYto.env.locallocally and to your hosting provider's environment settings in production.Terminalbashnpm install emailr - 04
Create a shared client
Create the client once in a server-only module. Install the
server-onlypackage; importing it makes the build fail if a Client Component ever imports this file.lib/emailr.tstsimport 'server-only' import { Emailr } from 'emailr' export const emailr = new Emailr({ apiKey: process.env.EMAILR_API_KEY! }) - 05
Send from a route handler
Route handlers are the right place for sends triggered by API calls or third-party services.
emails.send()resolves to{ success, message_id, recipients, status }and throws anEmailrError(withstatusCodeandcode) when the API rejects the request.app/api/welcome/route.tstsimport { emailr } from '@/lib/emailr' export async function POST(request: Request) { const { email, name } = await request.json() const result = await emailr.emails.send({ from: 'Acme <[email protected]>', to: email, subject: `Welcome, ${name}`, html: `<p>Hi ${name}, thanks for signing up.</p>`, tags: { category: 'welcome' }, }) return Response.json({ id: result.message_id }) } - 06
Or send from a server action
For forms, a server action avoids writing an API route at all.
app/contact/actions.tsts'use server' import { emailr } from '@/lib/emailr' export async function sendContactMessage(formData: FormData) { await emailr.emails.send({ from: 'Website <[email protected]>', to: '[email protected]', reply_to_email: String(formData.get('email')), subject: 'New contact form message', text: String(formData.get('message')), }) } - 07
Receive delivery events
Create a
transactionalwebhook in the dashboard pointing at/api/webhooks/emailr. Read the body withrequest.text()so the signature is computed over the exact bytes that were sent, then parse it.app/api/webhooks/emailr/route.tstsimport { createHmac, timingSafeEqual } from 'node:crypto' export async function POST(request: Request) { const body = await request.text() const signature = request.headers.get('x-emailr-signature') ?? '' const expected = createHmac('sha256', process.env.EMAILR_WEBHOOK_SECRET!) .update(body) .digest('hex') if (signature.length !== expected.length || !timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) { return new Response('Invalid signature', { status: 401 }) } const { event, data } = JSON.parse(body) if (event === 'email.bounced') { // mark the address as undeliverable in your database } return new Response('ok') }
Tips
- React Email or any other renderer works: render to an HTML string first and pass it as
html. - The webhook route uses
node:crypto, so keep it on the Node.js runtime (the default). - For bulk newsletters use Broadcasts instead of looping over
emails.send()in a request.
Frequently asked questions
Does the emailr_ SDK work with the Next.js App Router?
Yes. Use it in route handlers, server actions and other server-only code. It uses fetch under the hood and has no native dependencies.
Can I send email from a Client Component?
No. Sending requires your secret API key, which must stay on the server. Call a server action or route handler from the client instead.
Can I use React Email templates?
Yes. Render the component to an HTML string and pass it as the html field, or store the markup as an emailr_ template and send it with template_id and template_data.
How do I handle bounces in Next.js?
Subscribe a transactional webhook to your route handler, verify the X-Emailr-Signature header against the raw body, and handle email.bounced and email.complained events.
Keep going
Documentation
Related guides
- Send email from Vercel FunctionsSend transactional email from Vercel Functions with emailr_, manage keys with Vercel environment variables and schedule digest emails with Vercel Cron.
- Send email from RemixSend transactional email from Remix actions with the emailr_ SDK, keep the client in a .server module and verify delivery webhooks in resource routes.
- Send email from NuxtSend transactional email from Nuxt server routes with the emailr_ SDK, keep keys in runtimeConfig and verify delivery webhooks with raw bodies.
Start sending in minutes
Create a free account, verify your domain and send your first email. No credit card required.