Document Templates
Customise document layouts with Quill templates using the built-in code editor.
Every document type has two editing modes: Editor and Code. Editor mode uses visual toggles to control what appears on the document. Code mode gives you full control over the layout using Quill templates.
Editor vs Code mode
| Feature | Editor mode | Code mode |
|---|---|---|
| How it works | Toggle switches control each section | You edit the template directly |
| Skill needed | None | Basic HTML and Quill syntax |
| Customisation | Section-level (show/hide, colours, font) | Pixel-level (any layout you want) |
| Best for | Most stores | Stores with specific layout requirements |
Both modes produce the same PDF format. Switching between them does not lose your work. Your code template is preserved when you switch to editor, and your editor settings remain when you switch to code.
Switching modes
At the top of the document builder, click Editor or Code to switch. The first time you switch to code mode, the system generates a default template based on your current editor settings. You can edit this template or start fresh.
The code editor
Code mode shows a split view:
- Left panel: A code editor with syntax highlighting and autocomplete
- Right panel: A live PDF preview that updates as you type
The editor supports Quill template syntax inside HTML. As you type {{ or {%, autocomplete suggests available variables, tags, and filters.
Saving
Click the Save button or press Ctrl+S to save your template. The save only affects code mode. Your editor mode settings are separate.
Resetting
Click Reset to replace your template with the default. This regenerates the template from the current default for that document type. A confirmation dialog appears before the reset happens.
Validation
If your template has syntax errors, a warning appears below the editor with the error details. Fix the error before saving to ensure the PDF generates correctly.
Template syntax
Document templates use Quill, the platform's template language. Quill syntax works inside standard HTML. The template is rendered to HTML, then converted to PDF.
Output a variable
Use double curly braces to output a value:
<p>Order #{{ order.order_number }}</p>
<p>Total: {{ order.total_price | money }}</p>Conditionals
Use if, elsif, and else to show content based on conditions:
{% if config.showLogo %}
<img src="{{ store.logo_url }}" />
{% endif %}
{% if order.financial_status == 'paid' %}
<p>PAID</p>
{% elsif order.financial_status == 'pending' %}
<p>PENDING</p>
{% endif %}Loops
Use for to iterate over arrays:
{% for item in order.line_items %}
<tr>
<td>{{ item.title }}</td>
<td>{{ item.quantity }}</td>
<td>{{ item.price | money }}</td>
</tr>
{% endfor %}Filters
Filters transform output. Apply them with a pipe |:
| Filter | What it does | Example |
|---|---|---|
money | Format as currency | {{ order.total_price | money }} |
date | Format a date | {{ order.invoice_date | date }} |
upcase | Convert to uppercase | {{ labels.invoice | upcase }} |
downcase | Convert to lowercase | {{ store.name | downcase }} |
default | Fallback if empty | {{ order.note | default: "None" }} |
Available variables
The data reference panel in the code editor lists every variable. Here is a summary by group.
Order
| Variable | Type | Description |
|---|---|---|
order.order_number | number | Order number |
order.invoice_number | string | Invoice reference number |
order.invoice_date | string | Invoice date |
order.email | string | Customer email |
order.currency | string | Currency code (GBP, EUR, USD) |
order.financial_status | string | Payment status |
order.subtotal_price | number | Subtotal before tax and shipping |
order.total_price | number | Total amount |
order.total_tax | number | Tax amount |
order.total_discounts | number | Discount amount |
order.total_shipping | number | Shipping cost |
order.amount_paid | number | Amount already paid |
order.amount_due | number | Outstanding balance |
order.po_number | string | Purchase order number |
order.note | string | Order notes |
order.tax_inclusive | boolean | Whether prices include tax |
Customer and addresses
| Variable | Type | Description |
|---|---|---|
order.customer.name | string | Customer full name |
order.customer.email | string | Customer email |
order.billing_address.name | string | Billing contact name |
order.billing_address.lines | array | Billing address lines |
order.shipping_address.name | string | Shipping contact name |
order.shipping_address.lines | array | Shipping address lines |
order.shipping_method.name | string | Shipping method |
Line items
Use {% for item in order.line_items %} to loop through items:
| Variable | Type | Description |
|---|---|---|
item.title | string | Product title |
item.variant_title | string | Variant name |
item.sku | string | Product SKU |
item.quantity | number | Quantity ordered |
item.price | number | Unit price |
item.line_price | number | Line total |
item.total_discount | number | Discount on this line |
item.original_price | number | Price before discount |
item.discount_description | string | Discount code or reason |
Transactions
Use {% for tx in order.transactions %} to loop through payments:
| Variable | Type | Description |
|---|---|---|
tx.date | string | Transaction date |
tx.method | string | Payment method |
tx.amount | number | Amount |
tx.status | string | Status |
Fulfillments
Use {% for f in order.fulfillments %} to loop through shipments:
| Variable | Type | Description |
|---|---|---|
f.date | string | Fulfillment date |
f.carrier | string | Carrier name |
f.tracking_number | string | Tracking number |
f.tracking_url | string | Tracking URL |
f.service_name | string | Service name |
Store
| Variable | Type | Description |
|---|---|---|
store.name | string | Store display name |
store.company_name | string | Company name |
store.address | array | Company address lines |
store.vat_number | string | VAT registration number |
store.logo_url | string | Logo URL |
store.contact_email | string | Contact email |
store.payment_terms | string | Payment terms text |
store.footer_text | string | Footer text |
Bank details
| Variable | Type | Description |
|---|---|---|
bank.name | string | Bank name |
bank.account_name | string | Account holder name |
bank.sort_code | string | Sort code |
bank.account_number | string | Account number |
bank.iban | string | IBAN |
bank.swift | string | SWIFT/BIC code |
Labels
Labels are translated strings that change based on your document language setting. Examples: labels.invoice, labels.billTo, labels.shipTo, labels.total, labels.subtotal.
Config toggles
Config toggles reflect your editor mode settings. Use them to conditionally show sections: config.showLogo, config.showSku, config.showTracking, config.showBankDetails, and others.
Theme
| Variable | Type | Description |
|---|---|---|
theme.primary_color | string | Primary colour hex |
theme.accent_color | string | Accent colour hex |
theme.font_family | string | Font family name |
Example: simple invoice header
<div style="display: flex; justify-content: space-between; margin-bottom: 20px;">
<div>
{% if config.showLogo %}
<img src="{{ store.logo_url }}" style="max-height: 60px;" />
{% endif %}
<p style="font-size: 10px; color: #666;">{{ store.company_name }}</p>
</div>
<div style="text-align: right;">
<h1 style="font-size: 24px; color: {{ theme.primary_color }};">
{{ labels.invoice | upcase }}
</h1>
<p>{{ order.invoice_number }}</p>
<p>{{ order.invoice_date }}</p>
</div>
</div>Supported HTML elements
The PDF renderer supports a subset of HTML:
| Element | What it becomes |
|---|---|
div | Block container |
p, h1-h6 | Text with sizing |
span, strong, em, b, i, u | Inline text with style |
table, tr, td, th | Table layout |
img | Image |
a | Hyperlink |
hr | Horizontal rule |
br | Line break |
Supported CSS properties
The renderer supports common layout and text properties: color, background-color, font-size, font-weight, font-style, text-align, text-decoration, padding, margin, border, width, max-width, display (block/flex), flex-direction, flex, gap, justify-content, align-items, and opacity.
Use inline styles or a <style> block at the top of your template. Class selectors, ID selectors, and element selectors are supported.
Tips
- Start with the default template and modify it, rather than writing from scratch
- Use the data reference panel to find the exact variable name you need
- Test with a real order (not just the preview) to confirm all sections render correctly
- The
moneyfilter formats numbers with the order's currency symbol - Use
config.*toggles in your template to respect the editor mode settings, so merchants can still toggle sections even in code mode