Skip to content

Templates

Use outbound.template(name, options) on a send Email instance.

Terminal window
const welcome = outbound.template<{ user: { name: string } }>("welcome", {
subject: ({ user }) => `Welcome ${user.name}`,
text: ({ user }) => `Hello ${user.name}. Welcome to our app.`,
html: ({ user }) => `<p>Hello ${user.name}. Welcome to our app.</p>`,
})

The template name must contain only letters, numbers, dot, underscore, or dash. The method returns the normalized name.

Templates use one of two render modes:

  • static — content is rendered once at compile time. Use when the template content is fixed and does not depend on the send payload.
  • dynamic — content is rendered at send time with the payload. Use when the template content depends on send payload values.

Layeron detects the mode automatically:

  • A string subject, text, or html produces a static template.
  • A function subject, text, or html produces a dynamic template.
  • A React component with no parameters (component.length === 0) is static.
  • A React component that accepts a payload parameter is dynamic.

Override auto-detection with render: "static" or render: "dynamic".

Static string templates render at compile time.

Terminal window
const notice = outbound.template("notice", {
subject: "Service update",
text: "Our service will be down for maintenance at midnight.",
})

Dynamic string templates use a payload function. Payload values are resolved at send time.

Terminal window
const receipt = outbound.template<{ amount: number }>("receipt", {
subject: ({ amount }) => `Receipt for $${amount}`,
html: ({ amount }) => `<p>Your payment of $${amount} was received.</p>`,
})

Static template strings can use {{path.to.value}} placeholders. Layeron replaces them with values from the send payload.

Terminal window
const welcome = outbound.template("welcome", {
subject: "Welcome {{user.name}}",
text: "Hello {{user.name}}. Welcome to {{app.name}}.",
})
Terminal window
const result = await outbound.send({
from: "hello",
to: "ada@example.com",
template: "welcome",
payload: {
user: { name: "Ada" },
app: { name: "Layeron" },
},
})

Placeholder paths use dot notation. If a path is missing from the payload, sending fails with an error.

In HTML templates, placeholder values are HTML-escaped automatically.

Pass a React Email component with react or component.

Terminal window
import { Body, Html, Text } from "@react-email/components"
function WelcomeEmail({ user }: { user: { name: string } }) {
return (
<Html>
<Body>
<Text>Hello {user.name}</Text>
</Body>
</Html>
)
}
const welcome = outbound.template<{ user: { name: string } }>("welcome", {
render: "dynamic",
subject: ({ user }) => `Welcome ${user.name}`,
text: ({ user }) => `Hello ${user.name}`,
react: WelcomeEmail,
})

Use render: "static" for a React component that does not read the payload. The component renders once at compile time and reuses the same HTML for every send.

Terminal window
function Footer() {
return (
<Html>
<Body>
<Text>Thanks for using our service.</Text>
</Body>
</Html>
)
}
const footer = outbound.template("footer", {
render: "static",
react: Footer,
})

Dynamic React Email components render at send time with the payload. Use this for personalized content.

Terminal window
function InvoiceEmail({ customer, total }: { customer: string; total: number }) {
return (
<Html>
<Body>
<Text>Dear {customer},</Text>
<Text>Your invoice for ${total} is ready.</Text>
</Body>
</Html>
)
}
const invoice = outbound.template("invoice", {
subject: ({ customer }) => `Invoice for ${customer}`,
react: InvoiceEmail,
})

The React component renders to HTML, and Layeron generates a plain text version from the HTML output.

subject, text, and html in outbound.send(input) override the matching field from the registered template.

Terminal window
const result = await outbound.send({
from: "hello",
to: "ada@example.com",
template: "welcome",
payload: { user: { name: "Ada" } },
subject: "Special welcome for Ada", // overrides template subject
})

Every template must provide at least one of subject, text, html, react, or component. Sending fails at runtime when the resolved message lacks a subject or body content.

Local dev stores the rendered template output in the outbox JSON file. Template rendering behavior is the same as production — static templates render once, dynamic templates render per send.