Skip to main content

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 (⌘K on 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 date helper, 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-lines rule 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 t calls. 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 then t prints 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​

BlockData it readsNotes
Letterheadbrand.logo.primary, brand.nameThe primary logo from the brand kit, or the organisation name when there is no logo.
Window envelope addresscompany.name, company.street, company.zip, company.city; customer.name, customer.street, customer.zip, customer.cityReturn address line and recipient in an 85 × 45 mm field. PDF only; see below.
Reference blockcustomer.number, invoice.number, invoice.dateCustomer number, invoice number and date as a right-aligned table.
Address blockcustomer.name, customer.street, customer.zip, customer.city, customer.countryRecipient address without a fixed size.
Signature linesignature.place, signature.date, signature.nameA line with place, date and name below it.

Invoice and payment​

BlockData it readsNotes
Invoice headinginvoice.number, invoice.period_start, invoice.period_end"Invoice 2026-0042" and the service period.
Invoice tableinvoice.lines[] with description, qty, price, totalAmounts are formatted with money in the template's currency.
Totals blockinvoice.net, invoice.vat_rate, invoice.vat, invoice.totalNet, VAT and total, right-aligned.
Payment terms and bank detailspayment.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 codepayment.name, payment.iban, payment.bic, payment.amount, payment.referenceA GiroCode that banking apps scan to fill in the transfer; see epcQr.
Small business VAT notenone"No VAT is charged under the small business scheme of § 19 UStG."
Reverse charge notecustomer.vat_idFor business customers in other EU countries: the recipient owes the VAT.

Page and layout​

BlockData it readsNotes
Footer with page numberscompany.name, company.emailGoes into the Footer tab. PDF only.
Legal footerbrand.legal_footerThe legal footer from the brand kit, centred, in the Footer tab. PDF only.
Two-column layoutnoneTwo equal columns that stay side by side in print. The cursor starts in the first column.
Keep togethernoneContent inside is never split across two pages. PDF only.
Page breaknoneStarts a new page. PDF only.

Report​

BlockData it readsNotes
Cover pagereport.title, report.subtitle, report.dateTitle page followed by a page break. PDF only.
Chartsales.labels, sales.values, sales.captionA 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 figuresreport.kpis[] with label, value, changeA 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 label shows no legend; a pie or doughnut keeps its legend, which names the slices. A full Chart.js configuration in data and options still works and wins over the plain fields.
  • The chart is 480 × 240 pixels unless you pass width and height, and alt gives it a description a screen reader reads; see chart.

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.