Email with Resend

Configure Better Auth and contact email delivery through Resend on Cloudflare Workers.

Transactional email is Integrated with Resend. Verification, password reset, and contact messages share sendEmail() and React Email templates. A newsletter template is included only as an asset; there is no newsletter user flow.

Included flow

FlowCall siteTemplateRecipient
Email verificationsrc/lib/auth.tsverify-email.tsxNew user
Password resetsrc/lib/auth.tsforgot-password.tsxUser requesting reset
Contact formsrc/routes/api/contact.tscontact-message.tsxwebsiteConfig.mail.supportEmail
NewsletterNot wiredsubscribe-newsletter.tsxNone

src/lib/email/index.ts lazily constructs the Resend client when sendEmail() is first called. This request-time initialization is important on Workers, where server secrets are not safe to capture during top-level module evaluation. The Resend SDK sends through its HTTP API, which fits the Worker runtime.

Shared email-layout.tsx and email-button.tsx provide the React Email shell and call-to-action. The shipped subjects and templates are English; UI internationalization does not automatically localize email.

subscribe-newsletter.tsx is only a reusable template. The template has no subscription endpoint, subscriber table, confirmation, preference center, or unsubscribe flow.

Setup

Create a Resend key

For local development, add the server-only value to .env:

RESEND_API_KEY=re_your_key

For production, store it as a Worker secret:

pnpm wrangler secret put RESEND_API_KEY

Do not turn it into a VITE_* variable or commit it to wrangler.jsonc.

Verify the sending domain

Add the DNS records requested by Resend and wait for domain verification. Configure SPF, DKIM, and DMARC according to your delivery policy.

Set sender and support addresses

Update src/config/website-config.ts:

mail: {
	provider: "resend",
	fromEmail: "Example <noreply@mail.example.com>",
	supportEmail: "support@example.com",
}

fromEmail must be allowed by the verified Resend domain. supportEmail receives contact submissions.

Local behavior and errors

Without a valid key, sendEmail() catches the provider failure and returns { success: false, error }; no message is delivered. This lets the application boot for local UI/database work, but email-dependent verification and reset cannot be fully tested.

The current auth callbacks await sendEmail() without inspecting its structured result. The contact route also does not propagate a returned provider error, so it can report request success even when Resend rejected delivery. Before relying on email for a critical workflow, log a safe failure identifier and either propagate, retry, or queue the failure according to product policy.

The contact route Zod-validates name, email, subject, and message length, but it is public. Add rate limiting, spam/abuse controls, and observability before launch.

Customize templates

Edit files under src/lib/email/templates and keep the shared layout neutral enough for all transactional messages. Replace the hard-coded SoarStarter footer and current-year behavior when rebranding.

For localized mail, pass an explicit locale from the triggering flow and select the corresponding subject/template copy. Do not infer it from browser state in a delayed job. Store the user's chosen locale when reliable email localization matters.

Verify

  • Register a user and receive a verification message with a working production-origin link.
  • Request a password reset and complete it through the received link.
  • Submit the contact form and confirm the configured support inbox receives it.
  • Invalid contact payloads fail validation; spam controls reject abusive traffic.
  • Missing/invalid API keys produce visible server-side diagnostics without leaking message bodies or secrets.
  • Resend dashboard events, bounces, and complaints are monitored.

Production notes

Respect provider sender and rate limits. Add retry/queue semantics for critical mail, idempotency where repeated callbacks are possible, and alerts for sustained delivery failures. Avoid logging recipient data or message bodies. Keep auth URLs on the deployed origin and rotate the Worker secret through Wrangler.

On this page