· resend / react-email / email

How to send transactional emails with Resend and React Email

Step-by-step: install Resend and React Email, write a typed template, send from a Next.js route, and preview locally. Covers the React Email 6 import change.

By

1,268 words · 7 min read

If your TypeScript app needs to send fewer than 10,000 emails a month, use Resend with React Email. You get a modern TypeScript SDK, React components as email templates, a generous free tier, and no XML template language to fight. Two packages, one npm install, and you’re sending.

Who this is for

TypeScript or Next.js developers who need transactional email (welcome emails, password resets, receipts) and want to write templates in React rather than raw HTML or a drag-and-drop builder. If you need to send more than 10k emails a month, look at Postmark or Mailgun — their pricing is built for volume.

What we tested

resend v6.12.4, react-email v6.5.0 (React Email 6.0 shipped April 16, 2026), Next.js 15 App Router, Node.js 22. Every code snippet below runs against these exact versions.

1. Create a Resend account and API key

Sign up at resend.com. The free tier allows 3,000 emails per month and 100 emails per day — the daily cap is the binding constraint, not the monthly one.

Once in the dashboard:

  1. Go to API KeysCreate API Key.
  2. Give it a name (e.g. my-app-dev), set the permission to Sending access.
  3. Copy the key — you’ll only see it once.

Add it to your .env.local:

RESEND_API_KEY=re_...

Failure mode: Resend shows the key only on creation. If you navigate away before copying, revoke the key and create a new one.

2. Add and verify your domain

The [email protected] sender address works for initial testing, but every email you send to an address other than your own Resend account email will be rejected in production. You need a verified custom domain.

In the dashboard, go to DomainsAdd Domain and enter your domain (e.g. yourdomain.com). Resend gives you three DNS records to add:

TypePurpose
TXTSPF — authorizes Resend to send on your behalf
TXT (DKIM)DKIM — cryptographic signature for deliverability
MXNeeded for bounce tracking; optional but recommended

Copy each record value directly from your Resend dashboard — the values are account-specific and differ per domain. After adding all three, click Verify. DNS propagation can take up to 72 hours, though it’s usually under 15 minutes.

Failure mode: sending from [email protected] in production silently fails for recipients outside your account. The dashboard shows it as delivered, but it isn’t. Add your domain before going live.

3. Install the packages

npm install resend react-email

That’s the full install. Do not install @react-email/components — it was deprecated when React Email 6.0 shipped. All component imports now come from react-email directly. Every tutorial published before April 2026 that tells you to install @react-email/components is broken.

Failure mode: if you install @react-email/components alongside react-email, you’ll get two conflicting versions of the same components and TypeScript errors that are hard to diagnose. Remove it.

4. Write a React Email template

Create emails/WelcomeEmail.tsx:

import {
  Html,
  Head,
  Body,
  Container,
  Heading,
  Text,
  Button,
} from 'react-email'

interface WelcomeEmailProps {
  username: string
  loginUrl: string
}

export function WelcomeEmail({ username, loginUrl }: WelcomeEmailProps) {
  return (
    <Html>
      <Head />
      <Body style={{ fontFamily: 'sans-serif', background: '#f4f4f5' }}>
        <Container
          style={{
            maxWidth: '560px',
            margin: '40px auto',
            background: '#fff',
            borderRadius: '8px',
            padding: '32px',
          }}
        >
          <Heading style={{ fontSize: '24px', marginBottom: '16px' }}>
            Welcome, {username}
          </Heading>
          <Text style={{ color: '#374151', lineHeight: '1.6' }}>
            Your account is ready. Click below to sign in.
          </Text>
          <Button
            href={loginUrl}
            style={{
              background: '#000',
              color: '#fff',
              padding: '12px 24px',
              borderRadius: '6px',
              textDecoration: 'none',
              display: 'inline-block',
              marginTop: '16px',
            }}
          >
            Sign in
          </Button>
        </Container>
      </Body>
    </Html>
  )
}

All seven imports (Html, Head, Body, Container, Heading, Text, Button) come from react-email — one package, one import source. React Email compiles this component to table-based HTML that renders correctly across Gmail, Outlook, and Apple Mail.

Failure mode: importing from @react-email/components instead of react-email fails at runtime with a module-not-found error after you uninstall the deprecated package, or silently loads stale code if you left both installed.

5. Send from a Next.js API route

Create app/api/send/route.ts:

import { Resend } from 'resend'
import { WelcomeEmail } from '@/emails/WelcomeEmail'
import { NextResponse } from 'next/server'

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

export async function POST(request: Request) {
  const { username, email, loginUrl } = await request.json()

  const { data, error } = await resend.emails.send({
    from: 'Welcome <[email protected]>',  // must match verified domain
    to: [email],
    subject: `Welcome, ${username}`,
    react: <WelcomeEmail username={username} loginUrl={loginUrl} />,
  })

  if (error) {
    return NextResponse.json({ error }, { status: 400 })
  }

  return NextResponse.json({ id: data?.id })
}

The react field accepts your component directly — no renderAsync or string serialization needed. Resend renders it server-side. The { data, error } pattern mirrors fetch — always check error before trusting data.

The from address must use a verified domain (yourdomain.com here). The display name (Welcome) is optional but recommended for inbox recognition.

Failure mode: if from uses an unverified domain, Resend returns a 422 error with the message The gmail.com domain is not verified. Please, add and verify your domain on https://resend.com/domains. The fix is always the same: verify the domain first.

6. Preview templates locally

Add a script to package.json:

{
  "scripts": {
    "email:dev": "email dev --dir ./emails --port 3001"
  }
}

Run it:

npm run email:dev

This opens a browser at http://localhost:3001 with a live-reload preview of every template in ./emails/. You can switch between desktop and mobile views and toggle between rendered HTML and the raw source. No Resend account or API key required for local preview.

Failure mode: if the port conflicts with your Next.js dev server (both default to 3000), pass --port 3001 as shown above.

Gotchas

100 emails/day on the free tier. The monthly cap of 3,000 sounds generous, but 100/day is the hard ceiling. If your welcome sequence or a promotional blast hits 100 before midnight UTC, remaining sends fail with a 429 until the next day. Plan for this in staging before going live.

[email protected] is test-only. This sender is Resend’s shared address. It works for sending to your own verified Resend email during development, but it’s rate-limited and blocked for production use. Commit to adding your domain before writing any send code that you intend to ship.

React Email 6 import change. Every older tutorial and Stack Overflow answer that shows import { Button } from '@react-email/components' is wrong as of April 2026. The correct import is from 'react-email'. If you’re adapting an existing template, do a global find-replace on from '@react-email/components'from 'react-email'.

Cloudflare Workers environment variables. If you deploy your email-sending route to a Cloudflare Worker, process.env.RESEND_API_KEY is undefined. Use env.RESEND_API_KEY instead, where env is passed as the second argument to the Worker’s fetch handler. Update the Resend client construction accordingly:

// Cloudflare Workers
export default {
  async fetch(request: Request, env: Env) {
    const resend = new Resend(env.RESEND_API_KEY)
    // ...
  }
}

Verdict

Use Resend + React Email for any TypeScript app sending fewer than 10k emails per month. The SDK ships complete TypeScript types with a clean { data, error } return shape, React templates are a genuine improvement over raw HTML or MJML, and the free tier handles most indie apps without a credit card. For higher volume, Postmark and Mailgun are built for volume and price per thousand sends — but for the long tail of projects that send a few hundred emails a day, Resend is the right default. See the full Resend review for long-term pricing and deliverability data.