Next.js

Send email from Next.js with Delivrie: App Router route handlers, Server Actions, React Email templates, webhooks and deploying on Vercel.

Send from server code only: route handlers, Server Actions or getServerSideProps. Never from client components, because the API key would ship to the browser.

1. Install and add your key

npm install delivrie
DELIVRIE_API_KEY=dlv_your_key

On Vercel, add the same variable under Project → Settings → Environment Variables. Don't prefix it with NEXT_PUBLIC_, which would expose it to the browser.

Create one client to share:

import { Delivrie } from "delivrie";

export const delivrie = new Delivrie(process.env.DELIVRIE_API_KEY);

2a. Send from a route handler

import { delivrie } from "@/lib/delivrie";

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

  const { data, error } = await delivrie.emails.send({
    from: "Acme <hello@mail.acme.com>",
    to: email,
    subject: `Welcome to Acme, ${name}`,
    html: `<p>Hi ${name}, thanks for signing up.</p>`,
  }, { idempotencyKey: `welcome-${email}` });

  if (error) return Response.json({ error: error.message }, { status: error.statusCode || 500 });
  return Response.json({ id: data.id });
}
Warning Escape anything users type before putting it in html, or use React Email (below), which escapes for you.

2b. Or from a Server Action

import { delivrie } from "@/lib/delivrie";

async function sendMessage(formData: FormData) {
  "use server";
  const { error } = await delivrie.emails.send({
    from: "Website <forms@mail.acme.com>",
    to: "team@acme.com",
    replyTo: String(formData.get("email")),
    subject: "New contact form message",
    text: String(formData.get("message")),
  });
  if (error) throw new Error(error.message);
}

export default function Page() {
  return (
    <form action={sendMessage}>
      <input name="email" type="email" required />
      <textarea name="message" required />
      <button type="submit">Send</button>
    </form>
  );
}

React Email

Build templates as React components with React Email and send them with the react option:

npm install @react-email/components @react-email/render
import { Html, Body, Heading, Text, Button } from "@react-email/components";

export default function Welcome({ name }: { name: string }) {
  return (
    <Html>
      <Body style={{ fontFamily: "sans-serif" }}>
        <Heading>Welcome, {name}</Heading>
        <Text>Thanks for joining Acme.</Text>
        <Button href="https://acme.com/start">Get started</Button>
      </Body>
    </Html>
  );
}

Pass the component as react; the SDK renders it to HTML and plain text:

import Welcome from "@/emails/welcome";
import { delivrie } from "@/lib/delivrie";

export async function POST(req: Request) {
  const { email, name } = await req.json();
  const { data, error } = await delivrie.emails.send({
    from: "Acme <hello@mail.acme.com>",
    to: email,
    subject: "Welcome to Acme",
    react: <Welcome name={name} />,
  });
  return error ? Response.json(error, { status: 502 }) : Response.json(data);
}

Preview templates while you design them with npx react-email dev.

Receive webhooks

import { verifyWebhook } from "delivrie";

export async function POST(req: Request) {
  let event;
  try {
    event = await verifyWebhook({
      payload: await req.text(),
      headers: req.headers,
      secret: process.env.DELIVRIE_WEBHOOK_SECRET!,
    });
  } catch {
    return new Response("invalid signature", { status: 400 });
  }

  if (event.type === "email.bounced" || event.type === "email.complained") {
    // mark event.data.to as undeliverable in your database
  }
  return new Response("ok");
}

Add the endpoint in Webhooks as https://your-app.com/api/webhooks/delivrie and copy its signing secret into DELIVRIE_WEBHOOK_SECRET.

Edge runtime

The SDK works on the Edge runtime without changes: add export const runtime = "edge" to the route file.

Common problems

More guides