Building blocks
The editor's Blocks panel inserts ready-made parts of a document: a letterhead, an invoice table, payment terms, a page footer. Every block is written in the template's language (Jinja2, Liquid or Handlebars), so what you insert is ordinary template code that you can change like everything else.
Open the panel
Choose Blocks in the tab bar above the code. The panel opens beside the code and stays open on your next visit in the same browser.
- Search matches every word you type against the name and description, ignoring case and accents. Enter inserts the first result, Esc closes the panel, and the arrow keys move through the list.
- Preview: the block under the pointer renders at the bottom of the panel with its own sample data, the template's language settings and your organisation's brand kit and fonts. The line below the preview says where the block goes and what it adds.
- Command palette: every block is also a command. Press
Ctrl+K(⌘Kon a Mac) and type "Insert block".
In an image template the panel leaves out the blocks that only make sense in a PDF: the two footers, Keep together, Page break, the cover page and the window envelope address.
What inserting a block does
- Code: the block goes in at the cursor of the Template tab. Footer blocks go into the Footer tab. When a Header or Footer tab is open, other blocks go there too; when the CSS or Head tab is open, they go into the Template tab.
- Sample data: the fields the block reads are added to the data set on screen, without changing values you already have. Sample data only feeds the preview and completion: an API render uses the data of the request, so send these fields from your integration. A field the request leaves out prints as empty text, except a date: the blocks format dates with the
datehelper, which prints today's date for a missing value. - CSS: the block's rules are added to the CSS tab, but only for selectors your stylesheet does not define yet. A
.ff-linesrule of your own therefore stays as it is. Colours and heading fonts read the brand kit's variables (var(--brand-color-primary, #222)), with a neutral fallback when the kit is empty. - Translations: labels such as table headings or "Page 1 of 2" are
tcalls. The blocks bring their labels in English and German: the English texts are always added to the Translations panel, the German ones when the template's locale is German or the template already has a German dictionary. For another language, add the keys in the panel yourself; until thentprints the English text. Keys that exist already are not overwritten, so change a label there: the next time you insert the block, your text stays.
Header and footer blocks carry their styles inline, because Chromium renders the page header and footer without the template's stylesheet.
The blocks
Letter
| Block | Data it reads | Notes |
|---|---|---|
| Letterhead | brand.logo.primary, brand.name | The primary logo from the brand kit, or the organisation name when there is no logo. |
| Window envelope address | company.name, company.street, company.zip, company.city; customer.name, customer.street, customer.zip, customer.city | Return address line and recipient in an 85 × 45 mm field. PDF only; see below. |
| Reference block | customer.number, invoice.number, invoice.date | Customer number, invoice number and date as a right-aligned table. |
| Address block | customer.name, customer.street, customer.zip, customer.city, customer.country | Recipient address without a fixed size. |
| Signature line | signature.place, signature.date, signature.name | A line with place, date and name below it. |
Invoice and payment
| Block | Data it reads | Notes |
|---|---|---|
| Invoice heading | invoice.number, invoice.period_start, invoice.period_end | "Invoice 2026-0042" and the service period. |
| Invoice table | invoice.lines[] with description, qty, price, total | Amounts are formatted with money in the template's currency. |
| Totals block | invoice.net, invoice.vat_rate, invoice.vat, invoice.total | Net, VAT and total, right-aligned. |
| Payment terms and bank details | payment.amount, payment.name, payment.iban, payment.bic, payment.reference, invoice.due_date | "Please transfer … by …" and the account. Uses the same payment object as the QR code. |
| SEPA payment QR code | payment.name, payment.iban, payment.bic, payment.amount, payment.reference | A GiroCode that banking apps scan to fill in the transfer; see epcQr. |
| Small business VAT note | none | "No VAT is charged under the small business scheme of § 19 UStG." |
| Reverse charge note | customer.vat_id | For business customers in other EU countries: the recipient owes the VAT. |
Page and layout
| Block | Data it reads | Notes |
|---|---|---|
| Footer with page numbers | company.name, company.email | Goes into the Footer tab. PDF only. |
| Legal footer | brand.legal_footer | The legal footer from the brand kit, centred, in the Footer tab. PDF only. |
| Two-column layout | none | Two equal columns that stay side by side in print. The cursor starts in the first column. |
| Keep together | none | Content inside is never split across two pages. PDF only. |
| Page break | none | Starts a new page. PDF only. |
Report
| Block | Data it reads | Notes |
|---|---|---|
| Cover page | report.title, report.subtitle, report.date | Title page followed by a page break. PDF only. |
| Chart | sales.labels, sales.values, sales.caption | A bar chart in the brand kit's primary colour. The type is selected after inserting, so type line, pie or doughnut over it; see Charts from plain data. |
| Key figures | report.kpis[] with label, value, change | A row of cards, one per figure. |
Charts from plain data
The chart helper draws from plain data, so your integration sends numbers and no Chart.js configuration:
{
"sales": {
"caption": "Revenue per quarter",
"labels": ["Q1", "Q2", "Q3", "Q4"],
"values": [12000, 15500, 14200, 18900]
}
}
For several lines or bar groups, send series instead of values, each with a label and optionally a color:
{
"sales": {
"labels": ["Q1", "Q2"],
"series": [
{ "label": "2025", "values": [9800, 11200] },
{ "label": "2026", "values": [12000, 15500] }
]
}
}
- Values are read as numbers; anything else leaves a gap.
- A series without a colour takes the next colour of a fixed palette; the block passes the brand kit's primary colour for a single series. Each slice of a pie or doughnut gets its own colour, or one from a list you pass as
color. - One bar or line series without a
labelshows no legend; a pie or doughnut keeps its legend, which names the slices. A full Chart.js configuration indataandoptionsstill works and wins over the plain fields. - The chart is 480 × 240 pixels unless you pass
widthandheight, andaltgives it a description a screen reader reads; seechart.
German invoices and letters
Window envelope address
The block has the size of the address field of a DIN 5008 window envelope, 85 × 45 mm, with the return address in its first 5 mm. It does not position itself on the page: in Form B the field starts 45 mm below the top edge and 20 mm from the left edge. Put the block first in the template and set the page margins in Page setup so that it lands there, for example a top margin of 45 mm and a left margin of 20 mm, or add a top margin to .ff-window in the CSS tab.
Reference block beside the address
On its own, the reference block starts below the address. To place it to the right of the address, insert Two-column layout first and then one block into each column:
<div class="ff-columns">
<div><!-- Window envelope address --></div>
<div><!-- Reference block --></div>
</div>
VAT notes
The small business note and the reverse charge note contain common standard wording. Whether and how you have to state it depends on your situation, so check the text with your tax adviser. You can change it in the Translations panel (vat.small_business, vat.reverse_charge, vat.customer_id) without touching the template code.
Dates and amounts in all blocks follow the template's locale and currency from Page setup, for example 13.10.2026 and 3.760,40 € with de-DE.
Organisation partials
The last group of the panel lists your organisation's shared partials written for the template's language. Inserting one writes its include line ({% include 'letterhead' %} in Jinja2 and Liquid, {{> letterhead}} in Handlebars) instead of copying the code, so later changes to the partial reach the template. Its preview uses the data set on screen and the template's CSS and translations, because a partial has no sample data of its own.
A shared partial is not listed when the template version has a partial of the same name: that one would win the include. See how a name is resolved.