Samva is in early access — self-serve signup is limited. Have a team invite? Sign up with that email. Contact us for access.

Next.js

Send email with Next.js Server Actions and Route Handlers using the samva SDK.

Send email with Next.js

Use the samva SDK directly from Next.js server code. Server Actions work well for forms, Route Handlers work well for JSON endpoints, and both keep your API key server-side.

Samva sends from the verified sender configured on your account, so the payload has no from field.

Install

bun add samva server-only

Create a server-only client module:

// lib/samva.ts
import "server-only";

import { createClient } from "samva";

const apiKey = process.env.SAMVA_API_KEY;
if (!apiKey) {
  throw new Error("SAMVA_API_KEY is not set.");
}

export const samva = createClient({ apiKey });

Server Action form

Next.js Server Actions let a form call server code directly:

// app/contact/actions.ts
"use server";

import { samva } from "../../lib/samva";

export async function sendContactEmail(formData: FormData) {
  const email = String(formData.get("email") ?? "").trim();
  const message = String(formData.get("message") ?? "").trim();

  if (!email || !message) {
    throw new Error("Email and message are required.");
  }

  const safeMessage = message.replaceAll("<", "&lt;").replaceAll(">", "&gt;");

  await samva.messages.send({
    to: [{ email }],
    channel: "email",
    email: {
      subject: "Thanks for contacting us",
      html: `<p>Thanks for reaching out.</p><p>${safeMessage}</p>`,
      text: `Thanks for reaching out.\n\n${message}`,
    },
  });
}

Use useActionState and useFormStatus when the UI needs pending and error state.

Route Handler endpoint

Use an App Router Route Handler for JSON clients:

// app/api/send/route.ts
import { samva } from "../../../lib/samva";

export const runtime = "edge";

export async function POST(request: Request) {
  const body = (await request.json()) as { email?: unknown; message?: unknown };
  const email = typeof body.email === "string" ? body.email.trim() : "";
  const message = typeof body.message === "string" ? body.message.trim() : "";

  if (!email || !message) {
    return Response.json({ ok: false, error: "email and message are required" }, { status: 400 });
  }

  const safeMessage = message.replaceAll("<", "&lt;").replaceAll(">", "&gt;");

  let result;
  try {
    result = await samva.messages.send({
      to: [{ email }],
      channel: "email",
      email: {
        subject: "Thanks for contacting us",
        html: `<p>Thanks for reaching out.</p><p>${safeMessage}</p>`,
        text: `Thanks for reaching out.\n\n${message}`,
      },
    });
  } catch {
    return Response.json({ ok: false, error: "Failed to send message." }, { status: 502 });
  }

  return Response.json({ ok: true, result });
}

The SDK is fetch-based, so this send path can run in the Next.js Edge runtime. Choose Node instead when the surrounding handler needs Node-only database drivers, filesystem access, or other Node built-ins.

React Email

Render React Email to HTML, then send the strings with Samva:

import { render, toPlainText } from "react-email";
import { samva } from "./lib/samva";
import WelcomeEmail from "./emails/welcome";

const html = await render(<WelcomeEmail name="Ada" />);

await samva.messages.send({
  to: [{ email: "ada@example.com" }],
  channel: "email",
  email: { subject: "Welcome", html, text: toPlainText(html) },
});

See the React Email integration for the full templating workflow.

Full cookbook and example

The cookbook and example links resolve after the companion samva-integrations PR lands.

On this page