Getting started

Introduction

What lttr is, how a template becomes a message, and what the library leaves to you.

lttr is a Go library for email templates. Authors write each email in Comark: Markdown plus components such as ::statement, ::receipt and :button. You give the template a Go data type. lttr.Compile checks the template against that type once, at startup, and Render fills in the data and returns a *lttr.Message with Outlook-safe HTML, a plain-text part and an envelope, ready to send as MIME.

The library is brand-neutral. It holds no colours, fonts, logo, senders or footer copy of its own; you supply them as a Theme and a set of families. The only English it writes is a handful of generic defaults, such as the sign-off "Thanks," and the tile link "Read".

Follow a template through lttr

main.go
var orderShipped = lttr.MustCompile[OrderShipped](families, "order.shipped", src)

msg, err := orderShipped.Render(renderer, recipient, OrderShipped{OrderID: "A-1042"})

Three things meet in these two lines:

  • The template is Markdown with YAML frontmatter. The frontmatter names the template and its family and holds the subject and preheader. The body is a stack of modules, and loose Markdown becomes a letter module. Merge tags ({{order_id}}) and bindings (:order="order") refer to fields of your type.
  • The family is a kind of email you send, such as order updates or password resets. It fixes the sender, the sending stream, the header and footer, the footer copy, the modules its templates may not use and whether opens and clicks may be tracked. You collect families in a Registry, and Compile applies their rules.
  • The stream is how the family's mail is sent: Transactional, Security, Marketing, Outreach or Internal. A Router sends each stream through its own Sender, so marketing mail and password resets can go out through different senders and keep separate reputations.

Compile does everything that does not depend on data, and Render does the rest. A misspelt merge tag, an unknown module attribute or a module the family forbids is a compile error. A merge tag with no value and no fallback, an invalid URL or an empty subject is a render error. Render never returns a partial message.

See the pipeline

Each stage is its own package, and each one builds on the stages before it:

StagePackageWhenWhat it does
ParsecomarkCompileReads the Markdown, components, attributes and merge tags into an AST with line and column positions.
CheckirCompileTurns the AST into typed modules, applies the module rules, and resolves every merge tag and binding against your type.
ArrangelayoutRenderAdds the family's header and footer and sets the spacing between modules.
Drawrender/html, render/textRenderWrites the table-based HTML and the plain-text part from the same arranged document.
PackagelttrRender, MIMEBuilds the Message with its envelope and headers, and serialises it as multipart/alternative.

theme (palettes, fonts, the brand mark) and data (Money, Receipt and the other typed values) sit beside the pipeline, and every stage may use them.

Know what lttr does not do

  • No provider SDKs. lttr sends over SMTP, or gives you the raw MIME bytes with Message.MIME. SES, Postmark or anything else is a Sender you write, or an SMTP relay you point at.
  • No web fonts. Your theme names three CSS font stacks (serif, sans, mono). lttr writes no @font-face rule or font link, so the stacks should name fonts the recipient already has.
  • No visual editor. Templates are text files in your repository, reviewed and tested like code.
  • No layout control in templates. Templates choose modules. The layout decides the spacing, the family decides the header and footer, and colours come from the theme, so a template never sets a margin.

Learn where it came from

lttr was built at SULV for CaribHubs, its first consumer; the Quiet example is the design it was built to reproduce.

Go further

Installation

Add the module and the CLI.

Quick start

One file: a type, a template, a family, a render and a send.

Comark syntax

Components, spans, attributes and merge tags.

Modules

The catalogue of modules a template can use.

Families and streams

Senders, streams, chrome and footer copy.

Agent skill

Teach your coding agent to write lttr templates.
Copyright © 2026