Email with Resend
Send transactional email with Resend.
Transactional email is sent through Resend with React Email templates. A
single sendEmail helper wraps the Resend client; the auth and contact flows
call it with a rendered template.
What it provides
- A lazily-initialised Resend client (
src/lib/email/index.ts). - React Email templates for verification, password reset, and contact messages.
from/support addresses pulled fromwebsite-config.ts.
Important files
| File | Role |
|---|---|
src/lib/email/index.ts | sendEmail() helper + lazy Resend client |
src/lib/email/templates/verify-email.tsx | Email verification |
src/lib/email/templates/forgot-password.tsx | Password reset |
src/lib/email/templates/contact-message.tsx | Contact form message |
src/lib/email/templates/subscribe-newsletter.tsx | Newsletter template (asset only) |
src/config/website-config.ts | fromEmail, supportEmail |
The client
The Resend client is created on first use, so the app boots without
RESEND_API_KEY (email simply fails at send time until it's set):
let resend: Resend | null = null;
function getResend() {
if (!resend) resend = new Resend(process.env.RESEND_API_KEY);
return resend;
}
export async function sendEmail({ to, subject, react }: SendEmailOptions) {
const { data, error } = await getResend().emails.send({
from: websiteConfig.mail.fromEmail,
to,
subject,
react,
});
// returns { success, messageId } or { success: false, error }
}sendEmail never throws — it returns a result object, so callers decide how to
handle failures.
Wired flows
Three flows actually send mail in the template:
| Flow | Trigger | Template |
|---|---|---|
| Email verification | Sign-up (sendOnSignUp: true) | VerifyEmail |
| Password reset | Forgot-password request | ForgotPassword |
| Contact message | Contact form POST | ContactMessage |
The first two are wired through Better Auth in src/lib/auth.ts
(emailVerification.sendVerificationEmail and sendResetPassword); sign-up
requires verification (requireEmailVerification: true). The contact form posts
to /api/contact, which validates input with Zod and sends to
websiteConfig.mail.supportEmail. See
Authentication.
The newsletter is template-only. subscribe-newsletter.tsx is an included
React Email asset, but no route subscribes a user, persists an address, or
sends it — don't treat the newsletter input as a working flow. Wire your
own route + storage before relying on it.
Addresses
Both the sender and support addresses come from website-config.ts:
mail: {
provider: "resend",
fromEmail: "SoarStarter <noreply@mail.soarstarter.com>",
supportEmail: "contact@soarstarter.com",
},fromEmail must be on a domain you've verified in Resend. supportEmail is the
contact-form recipient (and surfaces as the site's contact address).
Setup
Get an API key
Create a Resend account and an API key, then set
RESEND_API_KEY in .env.
Verify your domain
Add and verify your sending domain in Resend, then point fromEmail at it.
Unverified domains can't send.
Test a flow
Register a new account locally and confirm the verification email arrives, or submit the contact form.
Production notes
- Send from a verified domain with SPF/DKIM configured for deliverability.
- Mind Resend's sending limits/tier for your volume.
- Monitor failures:
sendEmailreturns{ success: false, error }rather than throwing — log it where you call it. - Consider retries/queueing for critical mail; the helper sends inline.
