The template that broke quietly
Mail that lives in a vendor dashboard has no link to the code. A field gets renamed, and weeks later someone receives a message with a placeholder in it. Typed templates fail the build instead.
Invitations, notifications, export links, report deliveries.
Growth-OS sends mail for a small set of things: inviting a teammate, a notification someone subscribed to, a data export link, a dashboard report, a time-off note. Templates are typed components in the repository with a shared responsive layout, so changing one is an ordinary code change with review and history.
The mechanism end to end, as implemented in the repository.
Pick or write a template
Typed components take props for names, amounts and links. Five shared templates use one responsive layout, and the local preview shows desktop, mobile, text and source.
Send from the server
A server service or background job calls one helper with the component, a recipient and a subject. The helper reads server-only configuration, so it cannot end up in browser code.
It renders and delivers
The helper renders the component to HTML with a plain-text alternative and hands it to the provider. A provider error is rethrown so the calling job owns the retry.
Unconfigured is a skip, not a lie
Without a key or a from-address the helper logs a development warning and returns without claiming a delivery happened.
Interactive example
This walkthrough demonstrates the implemented backend contract without using an account, credentials, or provider calls.
Interactive example
Deterministic sample data only. No account, provider, or network connection is used by the interaction.
Without a shared foundation, every team rebuilds these edge cases, differently.
Mail that lives in a vendor dashboard has no link to the code. A field gets renamed, and weeks later someone receives a message with a placeholder in it. Typed templates fail the build instead.
Each feature spinning up its own client means from-addresses drift, errors are swallowed differently, and changing provider becomes a hunt. One helper is one place to change.
An app that crashes at boot without an email key forces everyone to either hold production credentials or wrap every send in a condition. Here a missing key turns a send into a logged skip.
Highlights
Five shared templates live in the repository as typed components with matching MJML variants. Renaming a field they use fails the build instead of sending a broken line to someone.
Every feature and background job sends through the same helper, so the from-address, rendering and error handling live in one place.
Without an email key a send logs a warning and returns. Local work and tests run without production credentials, and nothing pretends a message was delivered.
Engineering guarantees
This page previews what a template looks like; it never sends anything. Whether a message reaches an inbox depends on domain setup and the provider, which is outside this code. Campaigns in the workspace are a separate thing and still send nothing at all.
The boundary above is code, not a claim. These files carry the contract:
src/lib/email.tssrc/lib/email/templatessrc/features/privacy/jobs.tsThese capabilities share contracts and boundaries with this one.
The docs describe the same contracts this page demonstrates.