# Templates

> Compose a document tree with TSX, and compute structure with the same JavaScript you write everywhere else.

Source: https://docxcelerate.com/docs/essentials/templates/

A template is the tree that describes your document, given a name so a project
can point at it.

Nothing is rendered when you write it. Evaluating the JSX only builds elements —
which is what lets a component decide what it is later, once it actually has
data.

## The shape

```tsx
import { Document, Section, template } from "docxcelerate/template";
import { Greeting, NextSteps } from "./nodes/index.ts";
import type { DocumentData } from "./types.ts";

export const documentTemplate = template<DocumentData>(
  <Document id="tenancy-renewal" title="Tenancy Renewal">
    <Section id="opening" title="Opening">
      <Greeting />
    </Section>
    <Section id="closing" title="Closing">
      <NextSteps />
    </Section>
  </Document>,
);
```

What wires that JSX to Docxcelerate rather than React is one setting in
`tsconfig.json`:

```json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "docxcelerate/template"
  }
}
```

Scaffolded workspaces already have this, so you don't need a pragma at the top of
any file. If you're adding Docxcelerate to a project that points
`jsxImportSource` somewhere else, put
`/** @jsxImportSource docxcelerate/template */` on line one of the file to
override it just there.

## Computed structure

A template is written at module scope, where no data exists yet. So if your
structure depends on data, it belongs in a component:

```tsx
export const Enclosures: Section = () => {
  const [state] = useState((data: DocumentData) => ({ items: data.enclosures }));

  return (
    <Section id="enclosures" title="Enclosed">
      {state.items.map((item) => <Paragraph>{item.label}</Paragraph>)}
    </Section>
  );
};
```

`{condition && <Node />}` works the way you'd expect too — anything falsy in the
children is skipped.

### Ids name themselves

You rarely need to write one. A node with no `id` gets named after whatever
already says what it is — its heading, or the component that produced it:

```tsx
<Section title="Fees and funding">   {/* fees-and-funding */}
  <Greeting />                        {/* greeting        */}
  <SignOff />                         {/* sign-off        */}
  <Paragraph>Plain.</Paragraph>       {/* paragraph       */}
</Section>
```

If two nodes derive the same name they get numbered: `greeting`, `greeting-2`.
Two ids you **wrote** that collide are still an error, because that's a typo
rather than a repetition.

Names come from what a node is, never from where it sits. Insert a paragraph
above another and nothing below it gets renamed.

**When should you write one?** When you want to pin an address — a node a request
asks for by name, or one you expect to reference later. Otherwise leave it out.

The same `.map()` works whether the list is known now or only per request. See
[loops are published as loops](#loops-are-published-as-loops) below.

## Reusable pieces

A component is a plain function, so a house-style wrapper is a plain function
too. Components take children like anything else:

```tsx
export const Letterhead: Section<{ children?: Yield }> = ({ children }) => (
  <Section id="letterhead" title="">
    <Image id="logo" src={logoDataUri} alt="Ashcroft Housing" />
    {children}
  </Section>
);
```

A shared hook is the other half of this. `useSetPrompts` sets prompts on whatever
the *calling* component returns, so you can apply house style to a node the hook
doesn't own:

```tsx
export function useHouseVoice() {
  useSetPrompts({
    systemPrompt: "You write for a housing association. Plain English, second person.",
  });
}
```

Any component that calls `useHouseVoice()` gets that system prompt on the node it
returns — and can still override it with a prop.

## Document projects

A document project ties a template to its style and preview data through one
entrypoint, `document.project.ts`:

```tsx
import { defineDocumentProject } from "docxcelerate/document";
import { documentTemplate } from "./document.tsx";
import { documentStyle } from "./document-style.ts";
import type { DocumentData } from "./types.ts";

export default defineDocumentProject<DocumentData>({
  id: "tenancy-renewal",
  name: "Tenancy Renewal",
  version: "1.0.0",
  template: documentTemplate,
  style: documentStyle,
  previewData: {
    residentName: "Avery Mitchell",
    propertyRef: "Flat 4, Ashcroft House",
  },
});
```

`previewData` is what the preview app resolves against. Keep it realistic — short
names and placeholder cities hide the layout problems that real data will find.

## What changes when you publish

Everything above assumes you hold the data. That's true for a preview, a local
pack, or a document you write on the spot.

Publishing to [an engine](/docs/generation/endpoint/) is different. The artifact
gets built **once**, against stand-in values for a request nobody has made yet,
and then stored. Anything that depends on request data can't be settled during
that build — it has to travel to the engine and happen per document.

That sounds like it would change how you write components. Mostly it doesn't:
you keep writing ordinary `if`s and ordinary `.map()`s, and the build works out
what has to travel. There are three things worth understanding, and the build
enforces all three rather than letting a wrong document reach a recipient.

### Compute per document, not per build

Interpolating a value is fine while publishing. It becomes the token the engine
substitutes:

```tsx
<Paragraph id="greeting">Hello {state.name},</Paragraph>
// published as: "Hello {{data.name}},"
```

*Computing* on one isn't, because the value doesn't exist yet. Formatting a
currency, totalling a list, comparing a date — those need a **deriver**, which
the engine runs per document:

```tsx
<Paragraph
  id="balance"
  derivers={[
    derive("currencyLabel", { output: "balanceLabel", inputs: [dataRef("balanceDue")] }),
  ]}
>
  Your balance is {"{{derived.balanceLabel}}"}.
</Paragraph>
```

`useFormat` and `useDeriver` compute during the build instead, which is what you
want whenever the value is already known. Reach for a deriver when it isn't.

### Decisions travel, and you still write `if`

Write the conditional you'd write anywhere:

```tsx
if (state.overdue) {
  return <Paragraph id="overdue">This account is overdue.</Paragraph>;
}

return <Paragraph id="clear">Nothing outstanding.</Paragraph>;
```

The build compiles that into a condition the engine evaluates per recipient, so
both arms travel with the document. Ternaries and `{cond && <Node />}` compile the
same way.

One distinction to know: an arm has to yield a **node**. A conditional that picks
between two *strings* is a value, not a decision, and a value that varies per
document is a deriver's job.

**This needs the Docxcelerate transform in your build** — `docxcelerateTransform()`
for Vite, or `docxcelerateEsbuildTransform()` for esbuild, both from
`docxcelerate/transform`. Scaffolded workspaces already have it. Without it the
conditional still runs, but it decides once at build time, for everybody.

### Loops are published as loops

A branch has two arms and both can be published. A loop has as many as the
request has entries, and nobody knows that number until a document is written —
so the loop itself gets stored.

What you write is the ordinary `.map()`:

```tsx
const [visits] = useState((data: TenancyData) => data.visits);

return visits.map((visit) => <Paragraph id="visit">Visit: {visit.label}</Paragraph>);
```

With real data this is just `Array.prototype.map`. It walks the collection
immediately, so previews show the actual repetition rather than a description of
it. Publishing can't walk it, so the stand-in intercepts the same call, runs the
body once, and the loop travels to the engine intact.

You never write a `{{ctx…}}` token here. The entry your body was handed knows the
path it stands for, so the reference writes itself.

Anything that has to look at the entries before the body can be written —
`.filter()`, `.sort()`, `.length`, a `for` loop — can't be answered while
publishing. Those raise an error telling you to use a deriver, which runs where
the data is.

### Branches multiply

Publishing a branch stores both arms. Nesting branches multiplies what the
document carries, so there's a limit: `branchLimit`, 32 by default. Passing it
fails the build rather than producing a quietly enormous artifact.

If you hit it, lift the decision into a deriver that returns one value — or raise
the limit, if the document really is that conditional.
