Fileloom uses Handlebars as its templating engine. This guide covers the syntax you need to create dynamic PDFs.
Basic Output
Output variables using double curly braces:
<h1>Hello, {{name}}!</h1>
<p>Your order #{{orderNumber}} is confirmed.</p>
With data {"name": "John", "orderNumber": "12345"}:
<h1>Hello, John!</h1>
<p>Your order #12345 is confirmed.</p>
Nested Properties
Access nested object properties with dot notation:
<p>{{customer.name}}</p>
<p>{{customer.address.street}}</p>
<p>{{customer.address.city}}, {{customer.address.state}} {{customer.address.zip}}</p>
With data:
{
"customer": {
"name": "Acme Corp",
"address": {
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip": "94102"
}
}
}
HTML Escaping
By default, Handlebars escapes HTML characters for security:
Input: <script>alert('xss')</script>
Output: <script>alert('xss')</script>
To output raw HTML (use carefully):
Only use triple braces {{{ for HTML you trust. Never use it with user-generated content.
Conditionals
Simple If
{{#if isPaid}}
<span class="badge paid">PAID</span>
{{/if}}
If-Else
{{#if isPaid}}
<span class="badge paid">PAID</span>
{{else}}
<span class="badge unpaid">UNPAID</span>
{{/if}}
If-Else-If
{{#if isOverdue}}
<span class="badge overdue">OVERDUE</span>
{{else if isPending}}
<span class="badge pending">PENDING</span>
{{else}}
<span class="badge paid">PAID</span>
{{/if}}
Falsy Values
{{#if}} treats these as false:
false
undefined
null
"" (empty string)
0
[] (empty array)
Unless (Inverse If)
{{#unless isPaid}}
<p class="warning">Payment required</p>
{{/unless}}
Loops
Each
Iterate over arrays:
<table>
{{#each items}}
<tr>
<td>{{this.name}}</td>
<td>{{this.quantity}}</td>
<td>{{currency this.price "USD"}}</td>
</tr>
{{/each}}
</table>
With data:
{
"items": [
{"name": "Widget", "quantity": 2, "price": 29.99},
{"name": "Gadget", "quantity": 1, "price": 49.99}
]
}
Loop Variables
Inside {{#each}}, special variables are available:
| Variable | Description |
|---|
this | Current item |
@index | Zero-based index (0, 1, 2…) |
@first | True if first item |
@last | True if last item |
@key | Property name (for objects) |
{{#each items}}
<tr class="{{#if @first}}first-row{{/if}} {{#if @last}}last-row{{/if}}">
<td>{{add @index 1}}.</td>
<td>{{this.name}}</td>
</tr>
{{/each}}
Empty Arrays
Handle empty arrays with {{else}}:
{{#each items}}
<tr><td>{{this.name}}</td></tr>
{{else}}
<tr><td>No items found</td></tr>
{{/each}}
Iterating Objects
Loop through object properties:
{{#each person}}
<p>{{@key}}: {{this}}</p>
{{/each}}
With data {"person": {"name": "John", "age": 30}}:
<p>name: John</p>
<p>age: 30</p>
Using Helpers
Helpers transform or format data:
{{! Single argument }}
{{uppercase name}}
{{! Multiple arguments }}
{{currency amount "EUR"}}
{{! With string literals }}
{{formatDate date "MMMM D, YYYY"}}
{{! Nested helpers }}
{{currency (multiply price quantity) "USD"}}
Common Helpers
{{! Text }}
{{uppercase "hello"}} → HELLO
{{lowercase "HELLO"}} → hello
{{capitalize "john doe"}} → John doe
{{titleCase "john doe"}} → John Doe
{{! Numbers }}
{{currency 1234.5 "USD"}} → $1,234.50
{{formatNumber 1234567 0}} → 1,234,567
{{percentage 75 100}} → 75.0%
{{! Dates }}
{{formatDate date "YYYY-MM-DD"}} → 2024-12-15
{{formatDate date "MMMM D, YYYY"}} → December 15, 2024
{{now "YYYY"}} → 2024
{{! Math }}
{{add 10 5}} → 15
{{multiply 10 5}} → 50
{{round 3.7}} → 4
See Helpers Reference for all 70+ helpers.
Add comments that won’t appear in output:
{{! This is a comment }}
{{!--
This is a
multi-line comment
--}}
Whitespace Control
Control whitespace with ~:
{{#each items ~}}
{{this.name}}
{{~/each}}
The ~ removes whitespace on that side of the tag.
Partials (Reusable Snippets)
While Fileloom doesn’t support custom partials, you can achieve reusability with:
- CSS classes for consistent styling
- Helper functions for data transformation
- Template duplication for similar documents
Practical Examples
Invoice Line Items with Totals
<table class="items">
<thead>
<tr>
<th>Description</th>
<th>Qty</th>
<th>Price</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{{#each items}}
<tr>
<td>{{this.description}}</td>
<td>{{this.quantity}}</td>
<td>{{currency this.unitPrice "USD"}}</td>
<td>{{currency (multiply this.quantity this.unitPrice) "USD"}}</td>
</tr>
{{/each}}
</tbody>
<tfoot>
<tr>
<td colspan="3">Subtotal</td>
<td>{{currency subtotal "USD"}}</td>
</tr>
<tr>
<td colspan="3">Tax ({{percentage taxRate 1 0}})</td>
<td>{{currency tax "USD"}}</td>
</tr>
<tr class="total">
<td colspan="3">Total</td>
<td>{{currency total "USD"}}</td>
</tr>
</tfoot>
</table>
Conditional Sections
<div class="invoice-header">
<h1>Invoice #{{invoiceNumber}}</h1>
{{#if isPaid}}
<div class="stamp paid">
PAID
<span class="date">{{formatDate paidDate "MMM D, YYYY"}}</span>
</div>
{{else if isOverdue}}
<div class="stamp overdue">
OVERDUE
<span class="days">{{daysBetween dueDate}} days</span>
</div>
{{/if}}
</div>
<address>
{{customer.name}}<br>
{{#if customer.company}}{{customer.company}}<br>{{/if}}
{{customer.address.street}}<br>
{{#if customer.address.unit}}{{customer.address.unit}}<br>{{/if}}
{{customer.address.city}}, {{customer.address.state}} {{customer.address.zip}}<br>
{{#if customer.address.country}}{{customer.address.country}}{{/if}}
</address>