Limited offer: $250 off for the first 200 customers!Claim discount

Email

Transactional emails built with React Email and sent through Resend.

ResendResend
NoteResend is optional. If you do not set RESEND_API_KEY, magic-link emails still work because the auth route falls back to Supabase's mailer. Product emails (receipts, cancellations, lead magnet delivery, etc.) require an email provider and are skipped until one is configured.

Setup

  • Create an account at resend.com.
  • Add and verify a sending domain (a subdomain like mail.yourdomain.com is usually easiest).
  • Create an API key and store it safely (Resend only shows it once).
  • Add it to .env.local:
RESEND_API_KEY=re_xxxxxxxxxxxxxxxx

Configure the sender

Set the sender and reply address in config.mail. Use a verified domain before going live.

// src/config.ts
mail: {
  fromAdmin: "Your App <hello@yourdomain.com>",
  supportEmail: "support@yourdomain.com",
  replyTo: "support@yourdomain.com",
},

How emails are sent

1) Auth emails (magic links)

The /api/auth/magic-link route tries to send a custom email viasendEmail(). If RESEND_API_KEYis missing, it falls back to Supabase's own mailer.

2) Product emails (receipts, cancellations, lead magnet delivery)

These are sent through sendEmail() in src/libs/resend.ts using your templates in src/emails/.

Available templates

All templates are in src/emails/ and written as React components:

  • MagicLinkEmail.tsx — sent when a user requests a sign-in link.
  • WelcomeEmail.tsx — sent on first sign-up.
  • OrderConfirmationEmail.tsx — sent after a successful purchase, includes plan name and amount.
  • SubscriptionCancelledEmail.tsx — sent when a subscription is immediately terminated.
  • SubscriptionCancellationScheduledEmail.tsx — sent when cancellation is scheduled for end of billing period.
  • LeadMagnetEmail.tsx — for delivering lead magnets to waitlist subscribers.

Customizing a template

Open any file in src/emails/. Edit the JSX to change layout, colors, and copy. The EmailLayout wrapper is the shared outer frame — update it once to change branding across all emails.

All templates automatically display your logo in the email header. The logo is pulled from config.logoUrl and converted to an absolute URL using yourconfig.domainName. If logoUrl is empty, the header shows only the app name text instead.

Receiving replies

Resend is send-focused. For replies, set a real inbox in config.mail.replyTo (for example your support address).

Sending a custom email

import { sendEmail } from "@/libs/resend";
import { welcomeEmail } from "@/emails/WelcomeEmail";

const { subject, html } = welcomeEmail({ name: "Alex" });

await sendEmail({
  to: "user@example.com",
  subject,
  html,
});

Resend Audience (optional)

The boilerplate includes a ready-to-use addToResendAudience() function in src/libs/resend.ts. When RESEND_AUDIENCE_ID is set, it adds a contact to the specified Resend Audience so you can send broadcast emails or sequences to your subscriber list later.

RESEND_AUDIENCE_ID=aud_xxxxxxxxxxxxxxxx

The /api/lead route calls this automatically whenever a new lead is captured. If the variable is not set, the step is silently skipped — nothing else breaks. Find your audience ID in the Resend dashboard under Audiences.

TipResend Audiences are separate from transactional emails. Transactional emails (receipts, magic links) go through sendEmail(). Broadcast emails to your list go through the Audience — you compose and send them in the Resend dashboard or via their broadcast API.

Keep emails out of spam

Do

  • Send from a subdomain (for example mail.yourdomain.com).
  • Set up SPF, DKIM, and DMARC.
  • Add an unsubscribe link when appropriate.
  • Personalize emails with useful details (name, plan, account info).

Avoid

  • Spammy phrases like “free money” or “guaranteed income”.
  • Messy layouts, unusual fonts, and too many visual effects.
  • Links to unrelated or low-trust websites.
TipResend includes a short video during domain setup that walks through this step by step.