lttr

Typed email templates in Go

Authors write each email in Markdown with components. When your service starts, lttr checks every merge tag in every template against your Go types. Then it renders each message as Outlook-safe HTML, a plain-text part and MIME, ready to send.

One template, one real email

This is the order email from the worked example, next to the email lttr renders from it. {{driver.first_name}}, {{eta}} and the :order binding are checked against the template's Go type when it compiles, and a typo stops the service at startup, with the file, line and column.
templates/order.md
---
template: order.out_for_delivery
family: buyer
subject: Your order is on its way
preheader: "Arriving at {{destination}} by {{eta}} today."
footer:
  note: "Payment is held until you confirm delivery. Questions? Reply to this email or visit the [Help Centre]({site}/help)."
---

::statement{eyebrow="Order {{order_id}}"}
# On its way.

{{driver.first_name}} picked up your order at {{picked_up_at}} and will reach {{destination}} by {{eta}}.

:button[Track delivery]{href="{{track_url}}"}
::

::progress{steps="Confirmed, Packed, On its way, Delivered" current="On its way"}
::

::receipt{:order="order"}
::

::meta
### Deliver to
{{deliver_to.name}}
{{deliver_to.address}}

### Driver
{{driver.name}} · ★ {{driver.rating}}
{{driver.vehicle}}
::
The email gallery is not available
public/gallery/manifest.json was not found when the site was built. Run `pnpm gallery` in docs-site/ (it needs Go).

What you get

The library holds no brand of its own. Colours, fonts, logo, senders and footer copy are yours; lttr supplies the checks and the markup.
Checked at compile time
Compile checks every merge tag, binding, module and family rule against your Go type once, at startup. It returns every error at once, each at name:line:col.
Outlook-safe HTML
Table-based markup with inline styles, fixed-width Outlook tables and VML buttons, all emitted through conditional comments. Every URL is checked before it is written.
Dark mode and mobile
Your theme's dark palette applies under prefers-color-scheme: dark and is repeated for Outlook.com. Below 480px, multi-column modules stack.
Plain text from the same source
The text part is rendered from the same laid-out document as the HTML, not scraped from it: headings, links, tables and the footer each have a text form.
Streams and sending
Each family belongs to a stream. A Router sends each stream through its own Sender, refuses internal mail to outside addresses and honours your suppression list.
Live preview
lttr serve recompiles the templates from disk on every request and shows each email at 600 and 375 px, in light and dark, with its text part.

Ready to send your first one?

Install the module, copy the quick start, and you have a typed, compiled template rendering to HTML and text in one file.
Copyright © 2026