Prompt

Send Transactional Email in Next.js App Router

Technology / Api platforms1 observationLast seen Aug 13, 2026

Latest observation

Aug 13, 2026 · OpenAI APIWeb search: off

Below is a simple, production-friendly way to send transactional emails from a Next.js App Router app using a Route Handler and a mail provider like Resend (same pattern works for SendGrid, Nodemailer, etc.).


1) Install a mail library

Example with Resend:

npm install resend

2) Add environment variables

Create .env.local:

RESEND_API_KEY=your_api_key_here
FROM_EMAIL=Acme <no-reply@yourdomain.com>

In production, use a verified sender domain.


3) Create an email service

lib/email.ts

import { Resend } from "resend";

const resend = new Resend(process.env.RESEND_API_KEY);

export async function sendWelcomeEmail(to: string, name: string) {
  return resend.emails.send({
    from: process.env.FROM_EMAIL!,
    to,
    subject: "Welcome to Acme!",
    html: `
      <div style="font-family: Arial, sans-serif;">
        <h1>Hi ${name},</h1>
        <p>Thanks for signing up. We're happy to have you!</p>
      </div>
    `,
  });
}

4) Create an App Router API route

app/api/send-email/route.ts

import { NextResponse } from "next/server";
import { sendWelcomeEmail } from "@/lib/email";

export async function POST(req: Request) {
  try {
    const body = await req.json();
    const { email, name } = body;

    if (!email || !name) {
      return NextResponse.json(
        { error: "Missing email or name" },
        { status: 400 }
      );
    }

    const result = await sendWelcomeEmail(email, name);

    return NextResponse.json({ success: true, result });
  } catch (error) {
    console.error("Email send error:", error);
    return NextResponse.json(
      { error: "Failed to send email" },
      { status: 500 }
    );
  }
}

5) Call it from your UI

Example form in a client component:

app/page.tsx

"use client";

import { useState } from "react";

export default function HomePage() {
  const [email, setEmail] = useState("");
  const [name, setName] = useState("");
  const [status, setStatus] = useState("");

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setStatus("Sending...");

    const res = await fetch("/api/send-email", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ email, name }),
    });

    const data = await res.json();

    if (res.ok) {
      setStatus("Email sent!");
    } else {
      setStatus(data.error || "Something went wrong");
    }
  };

  return (
    <main style={{ padding: 24 }}>
      <form onSubmit={handleSubmit}>
        <input
          type="email"
          placeholder="Email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
        <br />
        <input
          type="text"
          placeholder="Name"
          value={name}
          onChange={(e) => setName(e.target.value)}
        />
        <br />
        <button type="submit">Send Email</button>
      </form>

      <p>{status}</p>
    </main>
  );
}

6) Important notes

Use server-side only

Keep email-sending logic in:

  • app/api/.../route.ts
  • or Server Actions
  • or server-only utility modules

Do not expose API keys in client components.

For transactional emails

Typical use cases:

  • welcome emails
  • password reset
  • order confirmations
  • OTP / verification codes

Add validation and rate limiting

For public endpoints, add:

  • input validation (zod)
  • rate limiting
  • CAPTCHA if needed

Alternative: Send from a Server Action

If you prefer not to create a route handler, you can send directly in a Server Action. This is nice for form submissions in App Router.


If you want, I can also give you:

  1. a Nodemailer version,
  2. a Server Action version, or
  3. a full password-reset email flow in Next.js App Router.

Observation history

Unlock observation history

Create a free Obsurfable account to unlock historical responses, model comparisons, and deeper brand intelligence.

How did Obsurfable measure this prompt?

Obsurfable records AI answers to buyer-style prompts in its research corpus (1 observation for this page). Metrics are distributions over observations, not a single static ranking.

Which AI systems does Obsurfable collect answers from?

OpenAI, ChatGPT, Google, Gemini, Google AI Mode, Anthropic, Claude, Perplexity, Grok, DeepSeek, Mistral, Copilot, and Meta AI.