@onyx/mail
Transactional email with React Email templates delivered through Resend.
@onyx/mail owns every transactional email the platform sends. It ships React
Email templates for the welcome, email verification, password reset,
organization invitation, invitation reminder, and notification digest emails,
and wraps them behind one typed factory, createMailer. When no Resend API key
is configured (typical in local dev), emails are logged to the console with
their action link instead of sent, so link-based flows like password reset
still work end to end.
Creating a mailer
The package has a single entry point. createMailer(options) returns a
Mailer, and the Mailer type is useful for passing the mailer into other
packages (for example @onyx/auth accepts one):
import { createMailer } from '@onyx/mail';
import { env } from 'cloudflare:workers';
const mailer = createMailer({
apiKey: env.RESEND_API_KEY,
from: env.MAIL_FROM,
});apiKey is optional. If it is omitted, every send becomes a [mail] console
log that includes the email's primary action URL.
apps/web already creates a mailer this way and hands it to the auth layer,
which sends the welcome, verification, reset, and invitation emails at the
right lifecycle moments. Job handlers also receive a ready-made mailer in
their JobContext (see @onyx/jobs).
Sender methods
Each method takes a typed options object and renders the matching template:
await mailer.sendWelcomeEmail({ to, name, appLink });
await mailer.sendVerificationEmail({ to, name, verifyLink });
await mailer.sendResetPassword({ to, name, resetLink });
await mailer.sendOrganizationInvitation({ to, inviterName, organizationName, inviteLink });
await mailer.sendInvitationReminder({ to, inviterName, organizationName, inviteLink });
await mailer.sendNotificationDigest({ to, organizationName, items, appLink });sendNotificationDigest is called by @onyx/notifications when a user's
email outbox flushes, so a burst of activity lands as one digest email. Its
items are NotificationDigestItem values (also exported from the package);
it is a no-op when items is empty, and uses the single item's title as the
subject when there is exactly one.
A failed Resend delivery throws, so callers decide whether a send failure should fail the surrounding request.
Configuration
| Name | Kind | Purpose |
|---|---|---|
RESEND_API_KEY | secret | Resend API key. Optional locally; without it emails are logged, not sent. |
MAIL_FROM | var in wrangler.jsonc | Sender address, e.g. Onyx <hello@yourdomain.com>. |
In production MAIL_FROM must use a Resend-verified domain. The default
onboarding@resend.dev only delivers to the Resend account owner.