@repo/mail (packages/mail). It renders React Email templates with translations from @repo/i18n and sends through one provider.
Sending
The package exports a single function:context is typed from the template’s props, without locale and translations, which the function supplies.
Templates
packages/mail/src/emails/index.ts registers the templates:
Shared building blocks are in
src/components/: Wrapper, Logo, PrimaryButton.
Rendering
getTemplate({ templateId, context, locale }) in lib/templates.ts:
- Loads the
mailtranslation scope for the locale withgetMessagesForLocale(locale, 'mail'). - Calls the component with the context plus
localeandtranslations. - Takes the subject from
translations[templateId].subject. For thenotificationtemplate a non emptycontext.titlereplaces it. - Renders twice with
@react-email/render: once to HTML and once to plain text.
Locale and direction
The default locale ishe, taken from @repo/i18n. Supported locales are every key of config.locales: he, en, ar, de, es, fr.
Templates are direction aware. ctaArrow(locale) in lib/direction.ts returns a left arrow for RTL locales and a right arrow for LTR ones, based on getLocaleDirection.
Auth emails take the locale from the NEXT_LOCALE cookie on the request. Notification emails use the user’s stored locale.
Provider
packages/mail/src/provider/index.ts exports one provider:
ZEPTOMAIL_API_URL (or the ZeptoMail default endpoint) with ZEPTOMAIL_TOKEN. The sender is params.from when given, otherwise MAIL_FROM.
The folder also contains console.ts, mailgun.ts, nodemailer.ts, plunk.ts, postmark.ts and resend.ts. They implement the same SendEmailHandler and are not exported. To switch provider, change the one export line and set that provider’s env variables. For local work, exporting ./console prints emails instead of sending them.
Env
These are read with
process.env inside the package and are not part of the core-api Zod schema. A missing token does not stop the server from booting. It makes every send fail and return false.
Team invitation email
apps/core-api/src/lib/team-invite-mail.ts is the one place domain code sends email. When a coach adds a team member, sendTeamInvitationEmail(prisma, options) sends the teamInvitation template with a sign-in link built as <APP_WEB_URL>/login?email=....
- The locale comes from the studio row. A value outside the six mail locales falls back to the mail package default.
- It is best effort. The team member is created whether or not the email goes out. Missing mailer credentials, a provider outage or a studio row that disappeared are logged and swallowed.
createTeamInviteMailer({ prisma, appUrl, logger })wraps it for injection. The AutoFit import worker receives it asmailer.
Adding a template
1
Write the component
Add
src/emails/MyTemplate.tsx. Accept BaseMailProps (locale, translations) plus your own props. Use Wrapper for the layout.2
Register it
Add it to
mailTemplates in src/emails/index.ts. The key is the templateId.3
Add translations
Add a block under the same key in
packages/i18n/src/translations/<locale>/mail.json for every locale, including subject. A missing key in a non default locale falls back to the Hebrew text.4
Rebuild
pnpm --filter @repo/i18n build copies the translation files into dist, and pnpm --filter @repo/mail build rebuilds the package. core-api consumes both from dist.