pealdevDOCS
Back to peal.dev

AUTH STARTER · TRANSACTIONAL EMAILS

Transactional emails

Resend + React Email. Templates as components, inline styles for Gmail's sake, a console fallback in dev.

Three emails are wired: magic link, password reset, and OTP. All three use the same send() helper, same Resend client, same React Email render pipeline. Adding more is one function call.

Where they live

  • src/lib/email.ts — sender helpers (sendMagicLinkEmail, sendResetPasswordEmail, sendOTPEmail).
  • src/lib/emails/magic-link-email.tsx — React Email template.
  • src/lib/emails/reset-password-email.tsx
  • src/lib/emails/otp-email.tsx

Customizing a template

Templates are plain React components with inline styles. Edit src/lib/emails/magic-link-email.tsx and ship. Use only the React Email primitives — div, p, a from @react-email/components — because Gmail discards most CSS.

Adding a new email

TypeScript
// 1. Create the template
// src/lib/emails/welcome-email.tsx
export function WelcomeEmail({ name }: { name: string }) {
  return (
    <Html>
      <Body style={...}>
        <Heading>Welcome, {name}</Heading>
      </Body>
    </Html>
  );
}

// 2. Add a sender in src/lib/email.ts
export async function sendWelcomeEmail(email: string, name: string) {
  return send({
    to: email,
    subject: `Welcome to ${authConfig.appName}`,
    react: WelcomeEmail({ name }),
  });
}

// 3. Call it from a Better Auth hook or a server action.

The console fallback is intentional

If RESEND_API_KEY is missing, send() logs the HTML to the server console instead of throwing. That way local dev never crashes because you haven't set up email. Just don't ship that way to prod.

Set up DKIM, SPF, and DMARC before launch

Even with Resend, deliverability hinges on DNS. Verify your sender domain in the Resend dashboard, paste the records into your DNS, wait 10 min. Send a test to a Gmail and a fresh Outlook — if either lands in spam, fix it BEFORE marketing brings traffic.

Resend dev mode tip

Resend's onboarding domain (onboarding@resend.dev) is fine for local dev — it works without DNS setup. Just remember to swap it for your real EMAIL_FROM before prod.