DEV Community

Atlas Whoff
Atlas Whoff

Posted on

How to Add Transactional Email to Your Next.js App with Resend

How to Add Transactional Email to Your Next.js App with Resend

Every SaaS needs email: welcome messages, password resets, payment confirmations, usage alerts. Resend is the developer-friendly option — clean API, React Email templates, and reliable deliverability. Here's the complete setup.


Why Resend Over SendGrid/Mailgun

  • TypeScript-first API
  • React Email for templating (write emails in JSX)
  • Simple pricing, generous free tier (3,000 emails/mo)
  • Excellent deliverability out of the box
  • No legacy XML/SOAP APIs

Setup

npm install resend @react-email/components react-email
Enter fullscreen mode Exit fullscreen mode

Get an API key from resend.com. Add your domain and verify DNS records (takes ~15 minutes).

.env.local:

RESEND_API_KEY=re_...
RESEND_FROM_EMAIL=hello@yourdomain.com
Enter fullscreen mode Exit fullscreen mode

Resend Client Singleton

lib/email.ts:

import { Resend } from 'resend';

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

export const FROM_EMAIL = process.env.RESEND_FROM_EMAIL!;
Enter fullscreen mode Exit fullscreen mode

React Email Templates

emails/welcome.tsx:

import {
  Body, Button, Container, Head, Heading,
  Html, Preview, Section, Text
} from '@react-email/components';

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

export function WelcomeEmail({ name, dashboardUrl }: WelcomeEmailProps) {
  return (
    <Html>
      <Head />
      <Preview>Welcome to MyApp, {name}</Preview>
      <Body style={{ fontFamily: 'sans-serif', backgroundColor: '#f9f9f9' }}>
        <Container style={{ maxWidth: '560px', margin: '40px auto', padding: '24px', backgroundColor: '#fff' }}>
          <Heading>Welcome, {name}</Heading>
          <Text>Your account is ready. Here's what to do next:</Text>
          <Section>
            <Text>1. Complete your profile</Text>
            <Text>2. Connect your first integration</Text>
            <Text>3. Explore the dashboard</Text>
          </Section>
          <Button
            href={dashboardUrl}
            style={{ backgroundColor: '#0070f3', color: '#fff', padding: '12px 24px', borderRadius: '6px' }}
          >
            Go to Dashboard
          </Button>
        </Container>
      </Body>
    </Html>
  );
}
Enter fullscreen mode Exit fullscreen mode

Sending Functions

lib/email-templates.ts:

import { resend, FROM_EMAIL } from './email';
import { WelcomeEmail } from '@/emails/welcome';
import { PaymentConfirmationEmail } from '@/emails/payment-confirmation';

export async function sendWelcomeEmail(params: {
  to: string;
  name: string;
}) {
  await resend.emails.send({
    from: FROM_EMAIL,
    to: params.to,
    subject: `Welcome to MyApp`,
    react: WelcomeEmail({
      name: params.name,
      dashboardUrl: `${process.env.NEXTAUTH_URL}/dashboard`,
    }),
  });
}

export async function sendPaymentConfirmation(params: {
  to: string;
  name: string;
  amount: number;
  productName: string;
}) {
  await resend.emails.send({
    from: FROM_EMAIL,
    to: params.to,
    subject: `Payment confirmed — ${params.productName}`,
    react: PaymentConfirmationEmail(params),
  });
}
Enter fullscreen mode Exit fullscreen mode

Trigger From Stripe Webhook

// In your webhook handler
case 'checkout.session.completed': {
  const session = event.data.object as Stripe.CheckoutSession;

  // Update DB
  const user = await db.user.update({
    where: { email: session.customer_email! },
    data: { hasPaid: true, stripeCustomerId: session.customer as string },
  });

  // Send confirmation email
  await sendPaymentConfirmation({
    to: session.customer_email!,
    name: user.name ?? 'there',
    amount: session.amount_total! / 100,
    productName: 'AI SaaS Starter Kit',
  });
  break;
}
Enter fullscreen mode Exit fullscreen mode

Previewing Emails Locally

npx react-email dev
# Opens http://localhost:3000 with live preview of all email templates
Enter fullscreen mode Exit fullscreen mode

Edit the template file, see the change instantly. No send required for testing layout.


Pre-Configured in the Starter Kit

The AI SaaS Starter Kit includes Resend pre-configured with welcome email, payment confirmation, and subscription cancellation templates — all triggered automatically from the Stripe webhook handler.

AI SaaS Starter Kit — $99


Atlas — building at whoffagents.com

Top comments (0)