Next.js Quickstart
Wire Mailstorm into a Next.js 14+ project. Server Actions for the modern App Router pattern, and a Route Handler for client-driven flows. Works on any deploy target — Vercel, self-hosted, Cloudflare Pages, etc.
Server-only: Mailstorm is called server-side. Don't import the SDK in a Client Component or expose the API key in client bundles. Use Server Actions, Route Handlers, or API routes (pages router).
1. Install
npm install @mailstorm/resend
2. Environment
MAILSTORM_API_KEY=ms_test_...
# or ms_live_... in production3. Server Action (App Router)
Recommended pattern for forms in Next.js 14+. The function runs only on the server; the API key never leaves it.
"use server"; import { Resend } from "@mailstorm/resend"; const resend = new Resend(process.env.MAILSTORM_API_KEY!); export async function sendWelcome(formData: FormData) { const email = formData.get("email") as string; const { data, error } = await resend.emails.send({ from: "hi@yourdomain.com", to: email, subject: "Welcome", html: "<p>Thanks for signing up.</p>", }); if (error) return { ok: false, error: error.message }; return { ok: true, id: data?.id }; }
import { sendWelcome } from "../actions/welcome"; export default function SignupPage() { return ( <form action={sendWelcome}> <input name="email" type="email" required /> <button>Sign up</button> </form> ); }
4. Route Handler (App Router)
Use this when a client component or third-party calls back into your API.
import { Resend } from "@mailstorm/resend"; import { NextResponse } from "next/server"; const resend = new Resend(process.env.MAILSTORM_API_KEY!); export async function POST(req: Request) { const body = await req.json(); const { data, error } = await resend.emails.send({ from: "alerts@yourdomain.com", to: body.email, subject: body.subject, html: body.html, }); if (error) return NextResponse.json({ error }, { status: 500 }); return NextResponse.json({ id: data?.id }); }
5. Webhooks
Listen for delivered, bounced, opened, clicked, complained, and inbound events. Configure the URL in your Mailstorm dashboard at /webhooks.
import { NextResponse } from "next/server"; import { createHmac, timingSafeEqual } from "node:crypto"; export async function POST(req: Request) { const sig = req.headers.get("X-Mailstorm-Signature"); const raw = await req.text(); const expected = createHmac("sha256", process.env.WEBHOOK_SECRET!) .update(raw) .digest("hex"); if (!sig || !timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) { return new Response("bad signature", { status: 401 }); } const event = JSON.parse(raw); // switch event.type and handle return NextResponse.json({ ok: true }); }
6. Vercel deploy notes
- Add
MAILSTORM_API_KEYto your Vercel project's Environment Variables (Production + Preview + Development). - Webhook secrets get a separate env var (
WEBHOOK_SECRET). - The default Vercel function runtime supports our SDK out of the box (no edge-runtime tweaks required for Node serverless).
7. React Email (templates)
Use the open-source React Email library to write emails as React components. It's framework-agnostic and pairs cleanly with Mailstorm:
npm install @react-email/components @react-email/render
import { Html, Heading, Text } from "@react-email/components"; export default function Welcome({ name }: { name: string }) { return ( <Html> <Heading>Hi {name}</Heading> <Text>Thanks for signing up.</Text> </Html> ); }
"use server"; import { render } from "@react-email/render"; import { Resend } from "@mailstorm/resend"; import Welcome from "@/emails/welcome"; const resend = new Resend(process.env.MAILSTORM_API_KEY!); export async function sendWelcome(name: string, email: string) { const html = await render(<Welcome name={name} />); return resend.emails.send({ from: "hi@yourdomain.com", to: email, subject: "Welcome", html, }); }
What's next
- Webhooks deep dive — event names, payload shapes, retry semantics
- Migrate from Resend — coming from
resendon npm