Liquid templating
Render dynamic pass fields, format values, and build wallet pass content with Liquid.
Liquid templating
Liquid combines fixed text with dynamic values from a pass. The Wallet Crew uses the DotLiquid implementation, plus custom date filters and the minify tag.
Use this reference after confirming the required data paths on a real pass.
Real-world examples
A loyalty card displays a safe, whole-number points balance.
A gift card displays its identifier, amount, and expiry.
An event ticket turns seat data into venue directions.
Confirm available data
Each render receives a data context. It can contain pass data, additional data, and values returned by connected providers.
Inspect View data on a real pass before writing a template. This payload is the source of truth for variable paths.
Additional data is suitable for visible pass content. Metadata supports operations and segmentation. It should not be treated as a wallet display source.
For the complete update and validation flow, see Update pass data in templates.
Common paths include:
Customer
firstName, lastName, address.city
Pass
serialNumber, tenantId, publicUrl, authenticationToken
Loyalty
loyalty.points, pendingLoyaltyPoints, loyalty.amounts
Gift card
voucher.amount, voucher.currency, id.y2.giftCardId
Event ticket
ticket.name, ticket.startDate, ticket.seatNumber, ticket.entrance
Collections
y2.tickets, y2.bons.loyaltyCertificates
Custom values
additionalData.<key>
Missing values render as an empty string. Use default when a visible fallback is required.
Syntax basics
Use {{ ... }} to print a value. Use {% ... %} for logic that does not print on its own.
Filters run left to right. Dot notation follows the nested structure in the data payload.
Conditions and loops
Liquid treats only nil and false as falsy. The number 0 and the string "false" are truthy. Compare optional numbers explicitly.
Guard loops with a collection-size check. This prevents empty section headings.
Inside a loop, forloop.index, forloop.first, forloop.last, and forloop.length are available.
Assignments, branches, and whitespace
Use assign to reuse calculated values. Use case for code-to-label mappings.
Use {%- and -%} to trim surrounding whitespace. This keeps multi-line templates compact in narrow wallet fields.
YAML values
Quote Liquid values containing YAML special characters. Use |- for multi-line values.
Custom filters
Date filters
All date filters parse the input as a date or time value, apply the operation, and return a DateTimeOffset. Chain them with | date: "format" when a string output is needed.
add_minutes
Adds the specified number of minutes.
add_hours
Adds the specified number of hours.
add_seconds
Adds the specified number of seconds.
add_days
Adds the specified number of days.
add_timespan
Adds a timespan in hh:mm:ss format.
All date filters use invariant culture for parsing and formatting.
Format filter
format
Applies a format string to any IFormattable value such as numbers, dates, and enums.
This renders a grouped integer such as 1,234 in invariant culture.
This renders a currency-style value with two decimals.
Custom tag
minify
minifyGenerates a short redirect URL from a rendered pass link.
The tag first renders the nested expression, then creates a short URL through The Wallet Crew redirect service, and finally outputs the shortened URL string.
Validate and troubleshoot templates
A variable renders blank
A missing path does not fail the render. It resolves to an empty string. Inspect View data on a real pass, then use default where needed.
A missing variable usually produces a blank field. Validate field paths against a real pass context before publishing a template change.
A date is shifted or formatted unexpectedly
Dates contain time-zone information. Format the intended output explicitly. Use add_hours only when a known offset is required.
format ignores the pass locale
format ignores the pass localeThis is expected. format uses invariant culture. Use date for locale-aware dates. Assemble locale-specific number or currency text in each locale file when needed.
A YAML file does not parse
Quote Liquid values containing :, {, #, or &. Use |- for multi-line values.
A condition behaves unexpectedly
The number 0 and string "false" are truthy. Compare optional numeric values explicitly.
Output has blank lines
Use whitespace-control hyphens around the tags.
Literal Liquid syntax is required
Use raw and endraw.
The template contains a syntax error
Invalid Liquid can render an error message instead of the intended value. Treat parse errors as template defects and fix them before publication.
Use the pass preview in the back-office to test Liquid expressions against real pass data before deploying a template change to production.
For render timing and failure handling outside Liquid itself, see How a pass is rendered.
Standard filters
Standard DotLiquid filters transform strings, numbers, dates, and collections.
Strings
default
{{ loyalty.points | default: 0 }}
upcase, downcase, capitalize
{{ firstName | capitalize }}
strip, lstrip, rstrip
{{ ticket.name | strip }}
append, prepend
{{ publicUrl | append: tenantId }}
replace, remove
{{ ticket.name | replace: "VIP", "Premium" }}
truncate, truncatewords, slice
{{ id.y2.customerId | slice: 0, 4 }}
split
{{ "a,b,c" | split: "," }}
Use an empty truncation suffix to create an initial:
Numbers and collections
floor, ceil, round
{{ loyalty.points | default: 0 | floor }}
plus, minus, times, divided_by, modulo
{{ loyalty.points | divided_by: 100 | floor }}
abs, at_least, at_most
{{ balance | at_least: 0 }}
size, first, last, join
{{ tags | join: ", " }}
sort, reverse, uniq, compact
{{ items | sort | reverse }}
map, where
{{ y2.tickets | map: "ticketNumber" | join: ", " }}
Dates, URLs, and HTML
The date filter formats date values for the pass locale.
{{ ticket.startDate | date: "D" }}
Monday, 27 May 2026
{{ ticket.startDate | date: "d" }}
27/05/2026
{{ ticket.startDate | date: "M" }}
27 May
{{ ticket.startDate | date: "t" }}
20:00
{{ ticket.startDate | date: "yyyy-MM-dd HH:mm" }}
2026-05-27 20:00
Use url_encode and url_decode for URL values. Use escape, escape_once, or strip_html for HTML email values.
For the complete filter list, see the DotLiquid reference.
Recipes
The following patterns solve common loyalty, gift-card, and event-ticket needs. Confirm all data paths before use.
Loyalty cards
Display points safely
default prevents a blank balance. floor prevents unwanted decimal points.
Show pending points separately
Only render the pending line when points await validation.
Create a repeating stamp counter
modulo resets the visible counter after each reward.
The same pattern can select a matching image:
Show progress to the next reward
Calculate the remaining points from an editable threshold.
Derive a tier name
This pattern works when the source provides only a points balance.
List available vouchers
Render vouchers on the back of the card. An else branch makes the empty state clear.
Gift cards
Reuse one identifier
Use the same value for the barcode and manual-entry fields.
Display a variable amount
This uses an optional gift amount and a safe fallback.
Keep amount and currency separate
Dedicated fields let wallet platforms apply local currency formatting.
Set the validity window
Use the source dates for expiry and relevance.
Event tickets
Expire after the event
Expiry on the following day avoids time-zone and late-arrival issues.
Set a relevance window
This helps wallet platforms surface the ticket at the right time.
Display the event date and time
Separate fields allow the wallet layout to prioritise date or time.
Map category codes
case converts source codes into readable, locale-specific labels.
Build venue directions
Clean optional fields before concatenating them. Whitespace control keeps the output compact.
List tickets on a multi-ticket pass
This is useful for grouped purchases and multi-event passes.
Create an authenticated deep link
Only use this pattern where the destination supports the pass authentication flow.
FAQ
Is Shopify Liquid fully supported?
No. The Wallet Crew uses DotLiquid plus the custom extensions documented on this page. Shopify-specific extensions should not be assumed to work unless they are documented here.
Last updated

