User guide · Email

Adopt your email templates

stet also manages the emails your product sends on its own — the welcome message when someone signs up, the receipt after they pay, the reminder before a trial ends. These are called transactional emails, and their words usually live inside template files only a developer can touch. Here is exactly what stet does with them:

  • Takes the words — subjects, body lines, button labels — and makes them editable copy, in bulk
  • Proves it changed nothing: every adopted email renders exactly as it did before
  • Never touches the design — templates stay code, so an email can't break in an editor's hands
✦ AI promptor hand the sweep to your agent — hand-written entries are first-class

Adopt the email templates in this project. For each template: list its visible text as named slots in content/descriptor.json (turning ${…} into {{…}} variables), copy today's wording into content/defaults.json, and rewrite the template file into a shell that reads the slots. Record the template folders in stet.config.json's email-surface list. Show me every diff before applying, and tell me if any template file is untracked by git so I can commit it first. Finish with npx stet email verify. Reference: https://getstet.xyz/llms.txt

You need stet installed. The first extract names the folders your templates live in — after that, stet remembers them. Nothing is applied without your review.

01The first time: name the folders

Point extract at where your emails live and name what to adopt. stet doesn't guess — like init in the quickstart, where things live is declared once, on purpose. Applying is what saves the paths into stet.config.json, so the first run carries both.

$ npx stet email extract 'src/emails/**' --apply welcome
welcome (src/emails/welcome.tsx)
  class: transactional — review
  render: WelcomeEmail — sampleProps {"name":"sample-name"}
  welcome__headline: "Welcome, {{name}}" — vars name
  welcome__body: "You're in — here's your first step."
  welcome__cta_label: "Get started"
⋮
email extract: run with --apply to write these declarations

02After that: just run it

With the paths remembered, a bare extract walks every template it can read and writes a proposal per template: the visible text as named slots, interpolations as variables, and the rewrite that turns the file into a shell around them. Nothing is applied yet — the same look-before-you-touch rhythm as stet scan in the adopt chapter.

When to re-run it:

  • Changing what an email says, or sending a different message — that's ordinary editing. Extract never runs.
  • Someone built a new kind of email — a new template file landed in code. Re-run extract and its words join the registry with the rest.
$ npx stet email extract

03Review and apply

Apply the proposals you accept — by name, or all at once. Two files take the result: content/descriptor.json records what exists — each template's slots and variables — and content/defaults.json holds today's wording for every slot. They're the same files the rest of your copy lives in.

A template extract couldn't read is skipped and reported; you write that entry by hand, and a hand-written entry counts exactly as much as a generated one.

$ npx stet email extract --apply welcome
wrote src/emails/welcome.tsx
wrote stet.config.json
email extract --apply: 1 template declared (3 slots)
⋮
next: stet email verify

04Prove the design didn't change

Your git history already holds the original template file from before the rewrite. verify renders that original and the new shell with the same sample data, and compares the output byte for byte. One byte different and it fails, naming the template.

That before comes from git, so commit your template files before you adopt them. extract warns you when one isn't tracked — a template with no commit behind it has nothing to prove against.

$ npx stet email verify
welcome: PASS — 175 bytes, identical

05Edit email copy like any other copy

The slots are keys now. Editors change the words, the template owns the markup, and every change goes through the same draft-and-publish flow as the rest of your copy — the edit chapter walks it.

$ npx stet draft welcome__headline --value "You're in — here's your first step"
$ npx stet publish welcome__headline