Skip to content
Writing
NewNext.jsReact EmailTypeScriptServer ActionsApp Router

Sending Transactional Email in Next.js 15 (App Router & React Email)

Render responsive React components to clean HTML strings and dispatch them via Next.js 15 Server Actions with zero external SMTP dependencies.

The modern Next.js 15 App Router email architecture

In Next.js 15, sending email from client-rendered components is a critical security anti-pattern that leaks credentials. The modern paradigm renders React Email components into HTML strings entirely on the server inside Server Actions or Route Handlers, dispatching payloads via HTTP fetch.

Execution ContextInvocation MechanismBest Use CaseKey Benefit
Server Actions'use server' function callForm submissions, signup mutationsCo-located progressive enhancement with zero client API route boilerplate
Route Handlersexport async function POSTWebhooks, external API triggers, background jobsDirect control over HTTP status codes, CORS headers, and streaming responses

1. Designing the React Email Component (WelcomeEmail.tsx)

Using @react-email/components, define a responsive email template with clean Tailwind styling and dark mode typography:

TYPESCRIPT
import { Html, Head, Body, Container, Text, Link, Preview } from '@react-email/components';
import * as React from 'react';

interface WelcomeEmailProps {
  name: string;
  loginUrl: string;
}

export function WelcomeEmail({ name, loginUrl }: WelcomeEmailProps) {
  return (
    <Html>
      <Head />
      <Preview>Welcome to SadaSend, {name}</Preview>
      <Body style={{ backgroundColor: '#09090b', fontFamily: 'monospace', color: '#fafafa', padding: '40px 20px' }}>
        <Container style={{ maxWidth: '560px', margin: '0 auto', border: '1px solid #27272a', padding: '32px', borderRadius: '4px' }}>
          <Text style={{ fontSize: '20px', fontWeight: 'bold' }}>Welcome aboard, {name}</Text>
          <Text style={{ fontSize: '14px', lineHeight: '1.6', color: '#a1a1aa' }}>
            Your account is provisioned. You can now mint scoped agent keys and configure sending domains.
          </Text>
          <Link href={loginUrl} style={{ display: 'inline-block', backgroundColor: '#10b981', color: '#000', padding: '10px 20px', borderRadius: '3px', textDecoration: 'none', fontWeight: 'bold' }}>
            Open Dashboard →
          </Link>
        </Container>
      </Body>
    </Html>
  );
}

2. Server Action Dispatch (actions/send-welcome.ts)

Render the component to HTML using render() and dispatch to SadaSend API with an idempotency key:

TYPESCRIPT
'use server';

import { render } from '@react-email/render';
import { WelcomeEmail } from '@/emails/WelcomeEmail';

export async function sendWelcomeAction(userEmail: string, userName: string) {
  const html = await render(WelcomeEmail({ name: userName, loginUrl: 'https://app.sadasend.com' }));

  const res = await fetch('https://api.sadasend.com/emails', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.SADASEND_API_KEY}`,
      'Content-Type': 'application/json',
      'Idempotency-Key': `welcome_${userEmail}`,
    },
    body: JSON.stringify({
      to: userEmail,
      subject: 'Welcome to SadaSend',
      html,
      text: `Welcome to SadaSend, ${userName}! Open your dashboard: https://app.sadasend.com`,
    }),
  });

  const data = await res.json();
  if (!res.ok) throw new Error(data.message || 'Failed to send');
  return { success: true, messageId: data.id };
}

Why native fetch beats legacy SMTP in serverless Next.js

  • Zero connection pooling overhead during Vercel or AWS Lambda scale-up.
  • fast HTTP dispatch latency across global edge PoPs.
  • Built-in edge compatibility without requiring Node.js net sockets.
Free plan

Building AI agents that send email?

Scoped API keys, per-key recipient allowlists, approval mode and a hosted MCP server with ten tools — on the free plan, without a card.