Transactional emails built with React Email and sent through Resend.
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.comis usually easiest). - Create an API key and store it safely (Resend only shows it once).
- Add it to
.env.local:
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxConfigure 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_xxxxxxxxxxxxxxxxThe /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.
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.