Email templates

Write an email template as one TSX file that Samva reads without running, so developers, agents, and the visual editor all edit the same source.

A template is one TSX file written in the static profile: ordinary JSX, HTML, and Tailwind classes that Samva reads without running. Compiling the file produces an IR, a JSON description of the email that holds the markup, the bindings to your input, the conditions, the loops, and the formatters. The visual editor, agents, previews, and every send render that IR, so nothing you write executes when a message goes out.

SML is the name of this toolkit: the TSX source in the static profile, the compiler that turns it into IR, and the tools that read it. You will see it in the @samva/markup package, the sml agent skill, and the /docs/sml URLs. A template you write is TSX; SML is not a second syntax to learn.

templates/welcome.tsx
import { defineTemplate } from "@samva/markup";
import { Email, Section } from "@samva/markup/email";
import { jsonSchema } from "@samva/markup/input-schema";

export default defineTemplate({
  id: "welcome",
  schema: jsonSchema<{ firstName: string; plan?: string }>({
    type: "object",
    properties: { firstName: { type: "string" }, plan: { type: "string" } },
    required: ["firstName"],
    additionalProperties: false,
  }),
  fixtures: {
    default: { firstName: "Ada", plan: "Pro" },
    "no-plan": { firstName: "Ada" },
  },
  email: {
    subject: (input) => `Welcome, ${input.firstName}`,
    preheader: () => "Your account is ready",
    body: (input) => (
      <Email>
        <Section className="px-8 py-6">
          <h1 className="text-2xl font-semibold">Welcome, {input.firstName}</h1>
          {input.plan && <p>You are on the {input.plan} plan.</p>}
        </Section>
      </Email>
    ),
  },
});

One file for developers and marketers

Developers and marketers work on the same .tsx file in the same Git repository, so a project moves between them with no export or conversion.

Developer with their own agentMarketer with the hosted agent
Starts fromsamva templates init, which creates a project on Samva and clones itA starter from the template gallery in the dashboard
Edits withClaude Code, Cursor, or any editorChat with the hosted assistant, plus direct edits on the canvas
SeesTSX and a local preview of every fixtureThe rendered email for each fixture
Checks withsamva templates check, render, and snapshotThe same checks, shown as findings
Saves withgit commit and git pushSave version, a Git commit on the same branch
Ships withsamva templates publish, then typed sends from the SDK or CLIPublish in the dashboard

Every edit, whoever makes it, lands as a revision of the same file, and conflicts surface for explicit resolution.

Why a static profile

Compiling a template reads the file and reports problems without executing anything, so every surface gives the same answer:

  • A binding to a field the input schema does not declare fails at compile time, with the nearest field name suggested.
  • Every fixture is validated against the schema before you can save or publish.
  • Sending renders the published IR against the payload you send. There is no code to run, so rendering is a pure function of the IR and the input.

The trade is that a body cannot compute. Logic the profile cannot express, such as a total or a sorted list, is computed by the caller and sent in the payload. There is no escape hatch.

Where to go next

Local preview, checks, and export work without Samva credentials, except that a project importing your brand needs credentials or a pulled brand snapshot. Publishing creates an immutable publication from an exact commit; each publication keeps the brand version it was built with. Raw HTML and text sending remains available independently of templates.

Related documentation

On this page