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 from website-config.ts.

Important files

FileRole
src/lib/email/index.tssendEmail() helper + lazy Resend client
src/lib/email/templates/verify-email.tsxEmail verification
src/lib/email/templates/forgot-password.tsxPassword reset
src/lib/email/templates/contact-message.tsxContact form message
src/lib/email/templates/subscribe-newsletter.tsxNewsletter template (asset only)
src/config/website-config.tsfromEmail, 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):

src/lib/email/index.ts
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:

FlowTriggerTemplate
Email verificationSign-up (sendOnSignUp: true)VerifyEmail
Password resetForgot-password requestForgotPassword
Contact messageContact form POSTContactMessage

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:

src/config/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: sendEmail returns { success: false, error } rather than throwing — log it where you call it.
  • Consider retries/queueing for critical mail; the helper sends inline.

On this page