Template variables and Liquid
The data a template can print for an order, a product or a customer, the brand object, the money, percent and br filters, and what Liquid in a document template does not support.
A template is HTML with Liquid tags. When a document is generated, the Liquid is filled with the data of one order, one product or one customer, plus your shop and your branding. This page lists what a template can print and which filters it can use.
The editor shows the same list for the template you have open: the Variables card under the code has a short introduction and the full list of variables to expand.
What every template sees
| Object | What it holds |
|---|---|
order, product or customer |
The one the template renders, set with Renders on the Settings tab |
shop |
Your store: shop.name, shop.email, shop.currency, shop.domain, shop.url, shop.address |
brand |
Your branding from Settings, see below |
language |
The language the document is made for, see Templates in several languages |
Print a value with double braces and loop over a list with for:
<h1>Invoice {{ order.name }}</h1>
{% for line in order.line_items %}
<p>{{ line.quantity }} x {{ line.title }} - {{ line.discounted_total | money }}</p>
{% endfor %}
<p>Total: {{ order.total_price | money }}</p>Order
For templates that render An order.
| Variable | What it holds |
|---|---|
order.name |
The order number, for example #1001 |
order.id |
The Shopify id of the order |
order.created_at, order.processed_at |
Dates, print them with the date filter |
order.email, order.phone |
Contact details on the order |
order.note, order.po_number, order.tags |
Note, purchase order number and tags |
order.customer_locale |
The language the customer ordered in |
order.currency |
The currency code |
order.financial_status, order.fulfillment_status |
For example PAID and UNFULFILLED |
order.customer |
first_name, last_name, name, email |
order.billing_address, order.shipping_address |
name, company, address1, address2, zip, city, province, country, country_code, phone |
order.shipping_method |
The title of the shipping line |
order.payment_gateways, order.discount_codes |
Lists of text |
order.subtotal_price, order.shipping_price, order.total_tax, order.total_discounts, order.total_price, order.total_refunded |
Amounts as numbers |
order.tax_lines |
A list, each with title, rate, price |
order.line_items |
A list, see the next table |
Each entry of order.line_items:
| Variable | What it holds |
|---|---|
title, variant_title, sku, vendor |
Text |
quantity |
Number |
unit_price, discounted_unit_price |
Price of one unit, before and after discounts |
total, discounted_total |
Line total, before and after discounts |
tax_lines |
A list, each with title, rate, price |
image_url |
The line item's image, when it has one |
A document carries up to 250 line items of an order.
Product
For templates that render A product.
| Variable | What it holds |
|---|---|
product.title, product.handle, product.vendor, product.product_type |
Text |
product.description_html |
The description as you wrote it, as HTML. Print it with the raw filter |
product.tags, product.status |
Tags and status, for example ACTIVE |
product.created_at, product.updated_at |
Dates |
product.url |
The product page in your online store, when it is published there |
product.image_url |
The featured image |
product.options |
A list, each with name and values |
product.currency, product.min_price, product.max_price |
The price range |
product.variants |
A list, each with id, title, sku, barcode, price, compare_at_price, inventory_quantity, options (each name and value) and image_url |
A document carries up to 100 variants of a product.
Customer
For templates that render A customer.
| Variable | What it holds |
|---|---|
customer.name, customer.first_name, customer.last_name |
Name |
customer.email, customer.phone |
Contact details |
customer.note, customer.tags |
Note and tags |
customer.locale |
The customer's language |
customer.created_at |
Date |
customer.address |
The default address, with the same fields as an order address |
customer.orders_count, customer.amount_spent, customer.currency |
Totals |
customer.orders |
The 20 most recent orders, newest first, each with id, name, created_at, financial_status, fulfillment_status, total_price |
The brand object
Your branding from Settings is available in every template, as the brand object and as CSS variables. See Branding your documents for where the values come from.
| Variable | What it holds |
|---|---|
brand.logo_url, brand.logo_width |
Your logo and the width it is printed at. logo_url is empty when no logo is uploaded |
brand.company_name, brand.address, brand.tax_id, brand.footer_text |
Your company details. Empty text when a field is not filled |
brand.primary_color, brand.accent_color, brand.text_color, brand.muted_color, brand.border_color |
Hex colors |
brand.font_family, brand.heading_font_family, brand.font_size |
Body font, heading font and base font size |
In CSS, use the variables instead: var(--brand-primary), var(--brand-accent), var(--brand-text), var(--brand-muted), var(--brand-border), var(--brand-font), var(--brand-heading-font) and var(--brand-font-size). The page already uses your body font, base size and text color, and your heading font on headings, so a template only needs them for its own accents.
{% if brand.logo_url %}
<img src="{{ brand.logo_url }}" width="{{ brand.logo_width }}" alt="">
{% else %}
<strong>{{ brand.company_name | default: shop.name }}</strong>
{% endif %}
<p>{{ brand.address | br }}</p>Filters
Amounts are plain numbers and text is escaped, so three filters of our own do the formatting:
| Filter | What it does | Example |
|---|---|---|
money |
Formats an amount as money, in the currency of the order, product or customer and for the language of the document | order.total_price with money prints an amount such as 64.70 with the currency symbol |
money: "USD" |
The same with a currency code you choose. It only changes the format, it does not convert the amount | |
percent |
Turns a rate into a percentage | A tax rate of 0.19 prints as 19% |
br |
Keeps the line breaks of a text with several lines | brand.address with br prints one line per row |
raw |
Prints HTML as it is, without escaping | product.description_html with raw |
The standard Liquid filters work too, for example date, default, upcase, plus, times, size, join and first.
{{ order.processed_at | date: "%Y-%m-%d" }}
{% for tax in order.tax_lines %}
{{ tax.title }} {{ tax.rate | percent }}: {{ tax.price | money }}
{% endfor %}
{{ product.description_html | raw }}The language variable
language holds the language the document is made for: the Language of the Flow action when it is filled, else the language of the order's customer or of the customer, else the template's main language. Amounts are formatted for it.
{% if language == "de" %}
<h1>Rechnung {{ order.name }}</h1>
{% else %}
<h1>Invoice {{ order.name }}</h1>
{% endif %}For more than a few words, give each language its own HTML instead. See Templates in several languages.
What Liquid in a template does not support
A document template is one self-contained HTML file. Some things you may know from Shopify themes are not available:
- No includes, partials or layouts. The tags
render,include,layoutandblockare refused when you save. - No theme objects or theme filters. Only the data on this page exists. An unknown filter is reported as an error with its line, it is not ignored.
- No scripts. JavaScript in a template does not run, neither in the preview nor in the PDF.
- Images need absolute URLs, for example a file from your Shopify Files or
brand.logo_url. - Limits. The HTML can be up to 200 KB and the CSS up to 100 KB, and a template has 3 seconds to render.
A variable that does not exist prints nothing. Check values that can be empty with if before you print a label for them.
Next steps
- The template editor - where you write and preview the template.
- Branding your documents - the values behind the brand object.
- Troubleshooting - template errors and missing data.
