Guides / Common recipes

Common recipes

Short, copy-paste patterns for the things most documents need.

Line items and totals

Drive a table from an array with {{#each}}. Compute the totals in your data, not the template, so the arithmetic stays in your code where you can test it.

<table>
  <thead>
    <tr><th>Item</th><th>Qty</th><th>Amount</th></tr>
  </thead>
  <tbody>
    {{#each items}}
      <tr>
        <td>{{description}}</td>
        <td>{{qty}}</td>
        <td>{{amount}}</td>
      </tr>
    {{/each}}
  </tbody>
</table>
<p class="total">Total: {{total}}</p>

Use {{@index}}, {{@first}}, and {{@last}} inside the loop for numbering or to drop the border on the last row.

Conditional sections

Show a block only when data is present, with an optional fallback.

{{#if discount}}
  <tr><td>Discount</td><td>-{{discount}}</td></tr>
{{/if}}

{{#unless paid}}
  <p class="stamp">UNPAID</p>
{{/unless}}

{{#if shippingAddress}}
  {{#with shippingAddress}}<p>{{line1}}, {{city}}</p>{{/with}}
{{else}}
  <p>Collect in store.</p>
{{/if}}

A variable used only inside {{#if}}/{{#unless}} is treated as optional, so omitting it does not cause invalid_data.

Multi-page documents

Content flows across pages automatically. Control the breaks with CSS:

.statement + .statement { break-before: page; } /* one statement per page    */
tr                        { break-inside: avoid; } /* keep rows intact          */
h2                        { break-after: avoid; }  /* keep a heading with its body */

Put repeating column headers in a <thead>; Chromium repeats it at the top of each page the table spans. See Page setup for sizing and breaks in detail.

Formatting

There are no formatting helpers in templates (no currency, no date). Format values in your code before you send them, which keeps locale, currency symbols, and rounding under your control.

const data = {
  total: new Intl.NumberFormat('en-IE', { style: 'currency', currency: 'EUR' }).format(1488),
  date: new Intl.DateTimeFormat('en-GB', { dateStyle: 'long' }).format(new Date()),
};
// → total: "€1,488.00", date: "18 June 2026"

Then reference the ready-made strings: {{total}}, {{date}}.

Logos and signatures

Embed images as data: URIs so the render never depends on an external host (the most reliable option for fidelity):

<img class="logo" src="data:image/png;base64,{{logoBase64}}" alt="" />

You can pass the base64 string in your data, or hard-code a fixed brand logo directly in the template. External https:// images also work, subject to the asset rules.

Page margins are zero, so pin a footer with position: fixed and it repeats on every page.

.footer {
  position: fixed;
  bottom: 8mm;
  left: 18mm;
  right: 18mm;
  color: #8a9a94;
  font-size: 11px;
}

Automatic page numbers ("Page X of Y") are not available today. See the note in Page setup.