Send email from Next.js (App Router) with Nodemailer

A Next.js route handler that sends mail over SMTP with Nodemailer, credentials in .env.local, running on the Node runtime (not Edge), with a contact-form example and the serverless caveats.

Install

shell
npm install nodemailer

Complete example

Next.js — STARTTLS on 587, credentials from the environment
// .env.local
SMTP_HOST=smtp.queensmtp.com
SMTP_PORT=587
SMTP_USER=your-smtp-username
SMTP_PASS=your-smtp-password

// app/api/contact/route.ts
import nodemailer from "nodemailer";
import { NextResponse } from "next/server";

export const runtime = "nodejs";   // SMTP needs the Node runtime, not Edge

const transporter = nodemailer.createTransport({
  host: process.env.SMTP_HOST,
  port: Number(process.env.SMTP_PORT || 587),
  secure: Number(process.env.SMTP_PORT) === 465,
  auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
});

export async function POST(req: Request) {
  const { name, email, message } = await req.json();
  if (!email || !message) return NextResponse.json({ ok: false }, { status: 400 });

  await transporter.sendMail({
    from: '"Website" <hello@yourdomain.com>',   // your verified domain — NOT the visitor's address
    replyTo: email,
    to: "support@yourdomain.com",
    subject: `Contact form: ${name}`,
    text: message,
  });
  return NextResponse.json({ ok: true });
}

What to know

  • Edge runtime and Cloudflare Pages cannot open SMTP sockets — export const runtime = "nodejs", or call the relay's HTTPS API.
  • Never put the visitor's address in From (it fails SPF/DMARC); use replyTo.
  • Vercel functions time out at 10 s on Hobby — keep the SMTP timeout below that or send from a queue.

Same code, different provider

Only the host, port and credentials change. The rules per provider are on the SMTP settings hub.

ProviderHostPortUsernamePasswordNote
QueenSMTP smtp.queensmtp.com587 (STARTTLS) / 465 (SSL) / 2525SMTP username from the dashboardSMTP password$5/year + $0.10 per 1,000; 100 free/day
Gmail smtp.gmail.com587 / 465you@gmail.comApp Password500/day, From must be the account
Amazon SES email-smtp.<region>.amazonaws.com587 / 465 / 2587SES SMTP usernameSES SMTP passwordsandbox 200/day until approved
Office 365 smtp.office365.com587user@yourdomain.commailbox passwordSMTP AUTH must be enabled; 30/min
SendGrid smtp.sendgrid.net587 / 465 / 2525apikeyAPI keyno free plan since 2025

Credentials for the example above

Create a free account, verify your sending domain (SPF + DKIM shown in the dashboard), add an SMTP credential, and paste it into the code. 100 emails a day free; $5/year plus $0.10 per 1,000 after that. Prefer HTTPS? The same account has a REST email API.

Get SMTP credentials Test your settings first

Other languages and frameworks

Examples tested 2026-08-30. If a library changed its API, tell us.

Next.js SMTP — questions

Usually the Edge runtime (no SMTP), missing environment variables in the Vercel project, or a blocked port. Force runtime = "nodejs" and use 587 or 465.