Rendering & previews
The system turns MJML source into final HTML in two situations: when you ask for a preview during authoring, and when something needs to send the email for real. Both go through the same pipeline.
The pipeline
- Load the template row.
- Resolve fragments. Every reusable component referenced by the template is spliced into the MJML.
- Compile MJML. The fully assembled markup is turned into responsive HTML.
- Substitute data. Fields like
{{ Order.OrderNumber }}are replaced with real values from the supplied model. - Apply translations. Translation keys are resolved against the active locale.
- Return HTML. A single self-contained HTML document.
Previews
A preview is "what would this look like with this data, in this language" — useful during authoring, QA, and design reviews.
- Supply a template row id, a locale, and sample data.
- Receive the final HTML.
- Open it in a browser, an email-testing tool, or paste it into Postman to inspect the output.
Previews target a specific version (any status, including the draft you are currently editing) and never send mail or persist anything.
Send-time rendering
When the application sends a real email it follows the same pipeline. Two entry points exist:
- Production sends are issued from backend code, which resolves a
templateKeyto the latest Published version and renders that row. - Testing sends go through the HTTP endpoint that takes a row id directly, so authors can dispatch a draft to a real inbox before publishing it.
In both cases the caller supplies the recipient's typed data and locale; the system produces the final HTML and hands it to the email provider.
Per-recipient localization
Real sends localize date/time output per recipient. A send can target many
recipients at once; the system groups them by presentation (timezone, language,
clock preference) and renders once per group, so the date and date_range
filters show each recipient their own local time without any
per-recipient logic in the template.
A preview or test send has no specific recipient, so those filters fall back to UTC and the 12-hour clock — the authored times may differ from what a live recipient eventually sees.
Performance
- Compiled HTML is cached for a short window keyed by template version and content hash, so back-to-back sends of the same email are cheap.
- Translations are cached in-process; a translations change becomes visible within minutes without a restart.
- Reusable fragments are loaded once per render and reused across every embed of the same component.
Failure modes
Anything wrong with the template (missing fragment, broken MJML, unknown field on the model) surfaces at save time, not at send time. A template that saves successfully is a template that renders successfully.