> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tipar.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Template basics

> How a template is shaped, how data fills it in, and the two rules that keep templates predictable.

A template is a JSON document that describes a PDF page. You send it to [`POST /generate`](/api-reference/generate) alongside a separate `data` object; Tipar walks the template, substitutes your data, and lays out the page.

```json theme={"dark"}
{
  "template": { "page": { "...": "..." } },
  "data": { "...": "..." }
}
```

## Two rules

Everything about the template language follows from two deliberate constraints.

<AccordionGroup>
  <Accordion title="No expressions, no math, no eval" icon="calculator">
    Templates only do `{{path}}` substitution against `data`. There is no arithmetic, no formatting helpers, no conditionals (beyond repeating table rows). Compute subtotals, totals, dates, and currency strings in **your** code and pass the finished values in `data`. This keeps templates safe to accept from anywhere and keeps rendering fast and predictable.
  </Accordion>

  <Accordion title="Nodes mirror layout, not meaning" icon="ruler-combined">
    Nodes are layout primitives — `column`, `row`, `text`, `table` — not semantic components like `invoice` or `lineItem`. You compose a document the way you'd compose a layout. It's a little more verbose than a purpose-built `invoice` block, but it's fully general: the same handful of nodes build invoices, certificates, contracts, and reports.
  </Accordion>
</AccordionGroup>

## Anatomy of a template

A template has exactly one top-level key, `page`:

```json theme={"dark"}
{
  "page": {
    "size": "A4",
    "margin": 30,
    "defaultTextStyle": { "fontSize": 10 },
    "header":  { "type": "..." },
    "content": { "type": "..." },
    "footer":  { "type": "..." }
  }
}
```

| Field              | Type                                     | Required | Notes                                                    |
| ------------------ | ---------------------------------------- | -------- | -------------------------------------------------------- |
| `size`             | `"A4"` · `"A3"` · `"Letter"` · `"Legal"` | no       | Default `A4`.                                            |
| `margin`           | number (points)                          | no       | Default `30`. Applied uniformly to all four sides.       |
| `defaultTextStyle` | [Style](/templates/text#style)           | no       | Page-wide text defaults. See the propagation note below. |
| `header`           | [Node](#the-node-catalog)                | no       | Repeats at the top of every page.                        |
| `content`          | [Node](#the-node-catalog)                | **yes**  | The page body — the only required field.                 |
| `footer`           | [Node](#the-node-catalog)                | no       | Repeats at the bottom of every page.                     |

<Note>
  All measurements are in **points** (1 point = 1/72 inch), the standard PDF unit. `A4` is 595 × 842 pt; `Letter` is 612 × 792 pt.
</Note>

<Warning>
  `defaultTextStyle` only propagates `fontSize` and `color`. The weight and slant flags (`bold`, `semiBold`, `italic`) apply **only** where you set them on a node's own `style` — so a page default can't accidentally bold your entire document.
</Warning>

## The node catalog

Every node carries a `"type"` discriminator. There are seven node types:

<CardGroup cols={2}>
  <Card title="column" icon="bars" href="/templates/layout#column">
    Vertical stack of child nodes.
  </Card>

  <Card title="row" icon="table-columns" href="/templates/layout#row">
    Horizontal band of sized cells.
  </Card>

  <Card title="text" icon="font" href="/templates/text#text">
    A single styled string.
  </Card>

  <Card title="richText" icon="text-width" href="/templates/text#richtext">
    Mixed-style runs and page-number tokens.
  </Card>

  <Card title="spacer" icon="arrows-up-down" href="/templates/layout#spacer">
    A fixed vertical gap.
  </Card>

  <Card title="image" icon="image" href="/templates/images">
    A base64-embedded image.
  </Card>

  <Card title="table" icon="table-cells" href="/templates/tables">
    A grid with a `forEach`-driven body.
  </Card>
</CardGroup>

Nodes nest freely: a `row` cell can hold a `column`, which holds `text` and a `table`, and so on.

## Interpolation

Anywhere a `text` value or rich-text span appears, you can interpolate data with **double braces**:

```json theme={"dark"}
{ "type": "text", "value": "Invoice {{invoice.number}} for {{customer.name}}" }
```

* **Dotted paths** walk objects: `{{invoice.billTo.name}}` reads `data.invoice.billTo.name`.
* **Whitespace inside the braces is fine**: `{{ invoice.number }}` is the same as `{{invoice.number}}`.
* **Inside a table body**, the current row element is bound to `item` — `{{item.description}}`. See [Tables](/templates/tables#repeating-rows-with-foreach).
* **Values must be scalars** — strings, numbers, booleans. A path that points at an object or array renders as empty.

<Warning>
  There is **no array indexing and no expressions**. `{{lines[0].name}}`, `{{total * 1.19}}`, and `{{upper customer.name}}` are not supported. If you need a value, compute it in your code and put it in `data`. To repeat content over an array, use a [table](/templates/tables).
</Warning>

### Missing data is an error

If a template references a path that isn't present in `data`, the request fails with **`422 Unprocessable Entity`** and lists every missing path — so you fix them all in one round trip rather than one at a time.

```json theme={"dark"}
{
  "title": "Template references missing data",
  "status": 422,
  "errors": [
    { "code": "template.missing_data", "message": "invoice.total" },
    { "code": "template.missing_data", "message": "customer.name" }
  ]
}
```

This is deliberate: a silently blank field on a customer's invoice is worse than a loud failure you catch in testing. See the [error reference](/api-reference/errors#422-unprocessable-entity).

## Next

<CardGroup cols={2}>
  <Card title="Layout" icon="layer-group" href="/templates/layout">
    Stack and arrange content with `column`, `row`, and `spacer`.
  </Card>

  <Card title="Text & styling" icon="font" href="/templates/text">
    Typography, colour, alignment, and page numbers.
  </Card>

  <Card title="Tables" icon="table-cells" href="/templates/tables">
    Repeat rows over your data.
  </Card>

  <Card title="Full schema" icon="book" href="/api-reference/template-schema">
    The exhaustive node-by-node reference.
  </Card>
</CardGroup>
