11 KiB
Liquid Syntax - Complete Reference
Tag Categories
Control Flow Tags
if/elsif/else/endif
{% if product.available %}
<button>Add to Cart</button>
{% elsif product.coming_soon %}
<p>Coming Soon</p>
{% else %}
<p>Sold Out</p>
{% endif %}
Operators:
==- equals!=- not equals>- greater than<- less than>=- greater than or equal<=- less than or equalcontains- substring or array containsand- logical ANDor- logical OR
Examples:
{% if product.price > 100 and product.available %}
Premium item in stock
{% endif %}
{% if product.tags contains 'sale' or product.type == 'clearance' %}
On sale!
{% endif %}
unless
Negated if statement:
{% unless customer.name == blank %}
Hello, {{ customer.name }}
{% endunless %}
{# Equivalent to: #}
{% if customer.name != blank %}
Hello, {{ customer.name }}
{% endif %}
case/when
Switch-case statement:
{% case product.type %}
{% when 'shoes' %}
<icon>👟</icon>
{% when 'boots' %}
<icon>👢</icon>
{% when 'sneakers' %}
<icon>👟</icon>
{% else %}
<icon>📦</icon>
{% endcase %}
Iteration Tags
for loop
{% for product in collection.products %}
{{ product.title }}
{% endfor %}
Modifiers:
{# Limit to first 5 #}
{% for product in collection.products limit: 5 %}
{{ product.title }}
{% endfor %}
{# Skip first 10 #}
{% for product in collection.products offset: 10 %}
{{ product.title }}
{% endfor %}
{# Reverse order #}
{% for product in collection.products reversed %}
{{ product.title }}
{% endfor %}
{# Combine modifiers #}
{% for product in collection.products limit: 5 offset: 10 %}
{# Items 11-15 #}
{% endfor %}
forloop object (available inside loops):
{% for item in array %}
{{ forloop.index }} {# 1-based: 1, 2, 3, ... #}
{{ forloop.index0 }} {# 0-based: 0, 1, 2, ... #}
{{ forloop.rindex }} {# Reverse 1-based: 3, 2, 1 #}
{{ forloop.rindex0 }} {# Reverse 0-based: 2, 1, 0 #}
{{ forloop.first }} {# true on first iteration #}
{{ forloop.last }} {# true on last iteration #}
{{ forloop.length }} {# Total number of items #}
{% endfor %}
Example usage:
{% for product in collection.products %}
{% if forloop.first %}
<h2>Featured Product</h2>
{% endif %}
<div class="product-{{ forloop.index }}">
{{ product.title }}
</div>
{% if forloop.index == 3 %}
<hr> {# Divider after 3rd item #}
{% endif %}
{% if forloop.last %}
<p>Showing {{ forloop.length }} products</p>
{% endif %}
{% endfor %}
break and continue
{% for product in collection.products %}
{% if product.handle == 'target' %}
{% break %} {# Exit loop entirely #}
{% endif %}
{% if product.available == false %}
{% continue %} {# Skip to next iteration #}
{% endif %}
{{ product.title }}
{% endfor %}
tablerow
Creates HTML table rows:
{% tablerow product in collection.products cols: 3 %}
{{ product.title }}
{% endtablerow %}
{# Output: #}
<table>
<tr class="row1">
<td class="col1">Product 1</td>
<td class="col2">Product 2</td>
<td class="col3">Product 3</td>
</tr>
<tr class="row2">
<td class="col1">Product 4</td>
...
</tr>
</table>
tablerow object:
{% tablerow product in products cols: 3 limit: 12 %}
{{ tablerow.col }} {# Current column (1-based) #}
{{ tablerow.col0 }} {# Current column (0-based) #}
{{ tablerow.row }} {# Current row (1-based) #}
{{ tablerow.index }} {# Item index (1-based) #}
{{ tablerow.first }} {# true on first item #}
{{ tablerow.last }} {# true on last item #}
{{ tablerow.col_first }} {# true on first column #}
{{ tablerow.col_last }} {# true on last column #}
{% endtablerow %}
paginate
For paginating large collections:
{% paginate collection.products by 12 %}
{% for product in paginate.collection.products %}
{% render 'product-card', product: product %}
{% endfor %}
{# Pagination controls #}
{% if paginate.pages > 1 %}
{{ paginate | default_pagination }}
{% endif %}
{% endpaginate %}
paginate object:
{{ paginate.current_page }} {# Current page number #}
{{ paginate.pages }} {# Total pages #}
{{ paginate.items }} {# Total items #}
{{ paginate.page_size }} {# Items per page #}
{{ paginate.previous.url }} {# Previous page URL (if exists) #}
{{ paginate.previous.title }} {# Previous page title #}
{{ paginate.previous.is_link }} {# Boolean #}
{{ paginate.next.url }} {# Next page URL (if exists) #}
{{ paginate.next.title }} {# Next page title #}
{{ paginate.next.is_link }} {# Boolean #}
{{ paginate.parts }} {# Array of page links #}
Custom pagination:
{% paginate collection.products by 20 %}
<div class="pagination">
{% if paginate.previous %}
<a href="{{ paginate.previous.url }}">← Previous</a>
{% endif %}
{% for part in paginate.parts %}
{% if part.is_link %}
<a href="{{ part.url }}">{{ part.title }}</a>
{% else %}
<span class="current">{{ part.title }}</span>
{% endif %}
{% endfor %}
{% if paginate.next %}
<a href="{{ paginate.next.url }}">Next →</a>
{% endif %}
</div>
{% endpaginate %}
Variable Assignment
assign
Single-line variable assignment:
{% assign sale_price = product.price | times: 0.8 %}
{% assign is_available = product.available %}
{% assign product_count = collection.products.size %}
{% assign full_name = customer.first_name | append: ' ' | append: customer.last_name %}
capture
Multi-line content capture:
{% capture product_title %}
{{ collection.title }} - {{ product.title }}
{% endcapture %}
{{ product_title }} {# "Summer Sale - Blue T-Shirt" #}
{% capture greeting %}
<h1>Welcome, {{ customer.name }}!</h1>
<p>You have {{ customer.orders_count }} orders.</p>
{% endcapture %}
{{ greeting }}
liquid (multi-statement)
Cleaner syntax for multiple statements:
{% liquid
assign product_type = product.type
assign is_on_sale = product.on_sale
assign sale_percentage = product.discount_percent
if is_on_sale
assign status = 'SALE'
else
assign status = 'REGULAR'
endif
echo status
%}
Template Inclusion
render
Isolated scope (preferred method):
{# Basic usage #}
{% render 'product-card', product: product %}
{# Multiple parameters #}
{% render 'product-card',
product: product,
show_price: true,
show_vendor: false,
css_class: 'featured'
%}
{# Render for each item #}
{% render 'product-card' for collection.products as item %}
{# Pass arrays #}
{% render 'gallery', images: product.images %}
Inside product-card.liquid:
{# Only has access to passed parameters #}
<div class="product {% if css_class %}{{ css_class }}{% endif %}">
<h3>{{ product.title }}</h3>
{% if show_price %}
<p>{{ product.price | money }}</p>
{% endif %}
{% if show_vendor %}
<p>{{ product.vendor }}</p>
{% endif %}
</div>
include
Shared scope (legacy, avoid in new code):
{% include 'product-details' %}
{# Can access all parent template variables #}
{# Harder to debug and reason about #}
section
Load dynamic sections:
{% section 'featured-product' %}
{% section 'newsletter-signup' %}
Utility Tags
comment
Multi-line comments:
{% comment %}
This entire block is ignored
by the Liquid renderer.
Use for documentation.
{% endcomment %}
{# Single-line comment #}
echo
Output shorthand (alternative to {{ }}):
{% echo product.title %}
{# Equivalent to: {{ product.title }} #}
raw
Output Liquid code without processing:
{% raw %}
{{ This will be output as-is }}
{% Liquid tags won't be processed %}
{% endraw %}
Useful for documentation or code examples.
Whitespace Control
Strip whitespace using hyphens:
{%- if condition -%}
Content (whitespace stripped on both sides)
{%- endif -%}
{{ "hello" -}}world
{# Output: helloworld (no space) #}
{{- product.title }}
{# Strips whitespace before output #}
{{ product.title -}}
{# Strips whitespace after output #}
Example:
{# Without whitespace control: #}
{% for item in array %}
{{ item }}
{% endfor %}
{# Output has newlines and indentation #}
{# With whitespace control: #}
{%- for item in array -%}
{{ item }}
{%- endfor -%}
{# Output is compact #}
Operator Precedence
Order of evaluation (right-to-left):
{% if true or false and false %}
{# Evaluates as: true or (false and false) = true #}
{% endif %}
IMPORTANT: No parentheses support in Liquid. Break complex conditions into variables:
{# ❌ DOESN'T WORK: #}
{% if (x > 5 and y < 10) or z == 0 %}
{# ✅ WORKS: #}
{% assign condition1 = false %}
{% if x > 5 and y < 10 %}
{% assign condition1 = true %}
{% endif %}
{% if condition1 or z == 0 %}
{# Logic here #}
{% endif %}
Performance Tips
- Cache repeated calculations:
{# ❌ Inefficient: #}
{% for i in (1..10) %}
{{ collection.products.size }} {# Calculated 10 times #}
{% endfor %}
{# ✅ Efficient: #}
{% assign product_count = collection.products.size %}
{% for i in (1..10) %}
{{ product_count }}
{% endfor %}
- Use
limitandoffsetinstead of iterating full arrays:
{# ❌ Inefficient: #}
{% for product in collection.products %}
{% if forloop.index <= 5 %}
{{ product.title }}
{% endif %}
{% endfor %}
{# ✅ Efficient: #}
{% for product in collection.products limit: 5 %}
{{ product.title }}
{% endfor %}
-
Prefer
renderoverincludefor better performance and variable scoping -
Use
liquidtag for cleaner multi-statement blocks
Common Gotchas
- No parentheses in conditions - Use variables instead
- Right-to-left evaluation - Be careful with operator precedence
- String concatenation - Use
appendfilter orcapturetag - Array/object mutation - Not possible; create new variables
- Integer division -
{{ 5 | divided_by: 2 }}returns2, not2.5 - Truthy/falsy values:
falseandnilare falsy- Everything else (including
0,"",[]) is truthy
Debugging Tips
- Output variable types:
{{ product | json }} {# Output entire object as JSON #}
{{ product.class }} {# Output object type #}
{{ variable.size }} {# Check array/string length #}
- Check for nil/existence:
{% if product.metafield %}
Metafield exists
{% else %}
Metafield is nil
{% endif %}
- Use default filter for safety:
{{ product.metafield.value | default: "Not set" }}
- Enable theme preview console to see Liquid errors in real-time