Partials
Share layout and repeated blocks across templates with partials that compile into each template and cost nothing at send time.
A partial is a function from props to JSX, in any project file. The compiler inlines it, so a partial adds nothing at send time and every template that uses it picks up an edit at its next publish.
import { fmt } from "@samva/markup/fmt";
export const OrderRow = ({
item,
currency,
}: {
item: { title: string; qty: number; price: number };
currency: string;
}) => (
<p className="my-2">
{item.title} x {item.qty}: {fmt.money(item.price * item.qty, currency)}
</p>
);import { defineTemplate } from "@samva/markup";
import { Email, Section } from "@samva/markup/email/components";
import { jsonSchema } from "@samva/markup/input-schema";
import { OrderRow } from "../partials/order-row";
export default defineTemplate({
id: "receipt",
schema: jsonSchema<{
currency: string;
items: { title: string; qty: number; price: number }[];
}>({
type: "object",
properties: {
currency: { type: "string" },
items: {
type: "array",
items: {
type: "object",
properties: {
title: { type: "string" },
qty: { type: "number" },
price: { type: "number" },
},
required: ["title", "qty", "price"],
additionalProperties: false,
},
},
},
required: ["currency", "items"],
additionalProperties: false,
}),
fixtures: {
default: { currency: "USD", items: [{ title: "Linen shirt", qty: 2, price: 42 }] },
},
email: {
subject: () => "Your receipt",
body: (input) => (
<Email>
<Section>
{input.items.map((item) => (
<OrderRow item={item} currency={input.currency} />
))}
</Section>
</Email>
),
},
});- Props are bindings too.
item.titleis checked against the schema through the call site, so a misspelled field fails in the template that passes the wrong data. - Arithmetic on bound numbers, like
item.price * item.qty, is allowed inside formatter arguments and conditions. It is the one computation the profile permits, because it is total and deterministic. - A partial may take
children, which is how layouts compose:<Card title="Your order">...</Card>. - A partial that renders itself is rejected. Nesting is limited by the IR's depth limit.
- The
partials/folder is a convention. Any file inside the project can be imported.
In the visual editor, opening a partial shows a warning that an edit applies to every template that uses it.
Related documentation
Conditions and repetition
Show or hide content with && and ?:, repeat rows with .map, filter and count lists, and lay out grids, all against the validated input.
Email components
Write content in ordinary HTML and use email components only where a client needs table layout, button fallbacks, or required alt text.