# Liquid Syntax - Complete Reference
## Tag Categories
### Control Flow Tags
#### if/elsif/else/endif
```liquid
{% if product.available %}
{% elsif product.coming_soon %}
Coming Soon
{% else %}
Sold Out
{% endif %}
```
**Operators:**
- `==` - equals
- `!=` - not equals
- `>` - greater than
- `<` - less than
- `>=` - greater than or equal
- `<=` - less than or equal
- `contains` - substring or array contains
- `and` - logical AND
- `or` - logical OR
**Examples:**
```liquid
{% 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:
```liquid
{% unless customer.name == blank %}
Hello, {{ customer.name }}
{% endunless %}
{# Equivalent to: #}
{% if customer.name != blank %}
Hello, {{ customer.name }}
{% endif %}
```
#### case/when
Switch-case statement:
```liquid
{% case product.type %}
{% when 'shoes' %}
👟
{% when 'boots' %}
👢
{% when 'sneakers' %}
👟
{% else %}
📦
{% endcase %}
```
### Iteration Tags
#### for loop
```liquid
{% for product in collection.products %}
{{ product.title }}
{% endfor %}
```
**Modifiers:**
```liquid
{# 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):**
```liquid
{% 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:**
```liquid
{% for product in collection.products %}
{% if forloop.first %}
Featured Product
{% endif %}
{{ product.title }}
{% if forloop.index == 3 %}
{# Divider after 3rd item #}
{% endif %}
{% if forloop.last %}
Showing {{ forloop.length }} products
{% endif %}
{% endfor %}
```
#### break and continue
```liquid
{% 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:
```liquid
{% tablerow product in collection.products cols: 3 %}
{{ product.title }}
{% endtablerow %}
{# Output: #}
| Product 1 |
Product 2 |
Product 3 |
| Product 4 |
...
```
**tablerow object:**
```liquid
{% 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:
```liquid
{% 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:**
```liquid
{{ 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:**
```liquid
{% paginate collection.products by 20 %}
{% endpaginate %}
```
### Variable Assignment
#### assign
Single-line variable assignment:
```liquid
{% 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:
```liquid
{% capture product_title %}
{{ collection.title }} - {{ product.title }}
{% endcapture %}
{{ product_title }} {# "Summer Sale - Blue T-Shirt" #}
{% capture greeting %}
Welcome, {{ customer.name }}!
You have {{ customer.orders_count }} orders.
{% endcapture %}
{{ greeting }}
```
#### liquid (multi-statement)
Cleaner syntax for multiple statements:
```liquid
{% 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):
```liquid
{# 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:**
```liquid
{# Only has access to passed parameters #}
{{ product.title }}
{% if show_price %}
{{ product.price | money }}
{% endif %}
{% if show_vendor %}
{{ product.vendor }}
{% endif %}
```
#### include
Shared scope (legacy, avoid in new code):
```liquid
{% include 'product-details' %}
{# Can access all parent template variables #}
{# Harder to debug and reason about #}
```
#### section
Load dynamic sections:
```liquid
{% section 'featured-product' %}
{% section 'newsletter-signup' %}
```
### Utility Tags
#### comment
Multi-line comments:
```liquid
{% comment %}
This entire block is ignored
by the Liquid renderer.
Use for documentation.
{% endcomment %}
{# Single-line comment #}
```
#### echo
Output shorthand (alternative to `{{ }}`):
```liquid
{% echo product.title %}
{# Equivalent to: {{ product.title }} #}
```
#### raw
Output Liquid code without processing:
```liquid
{% 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:
```liquid
{%- 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:**
```liquid
{# 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):**
```liquid
{% 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:
```liquid
{# ❌ 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
1. **Cache repeated calculations:**
```liquid
{# ❌ 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 %}
```
2. **Use `limit` and `offset` instead of iterating full arrays:**
```liquid
{# ❌ 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 %}
```
3. **Prefer `render` over `include`** for better performance and variable scoping
4. **Use `liquid` tag** for cleaner multi-statement blocks
## Common Gotchas
1. **No parentheses in conditions** - Use variables instead
2. **Right-to-left evaluation** - Be careful with operator precedence
3. **String concatenation** - Use `append` filter or `capture` tag
4. **Array/object mutation** - Not possible; create new variables
5. **Integer division** - `{{ 5 | divided_by: 2 }}` returns `2`, not `2.5`
6. **Truthy/falsy values:**
- `false` and `nil` are falsy
- Everything else (including `0`, `""`, `[]`) is truthy
## Debugging Tips
1. **Output variable types:**
```liquid
{{ product | json }} {# Output entire object as JSON #}
{{ product.class }} {# Output object type #}
{{ variable.size }} {# Check array/string length #}
```
2. **Check for nil/existence:**
```liquid
{% if product.metafield %}
Metafield exists
{% else %}
Metafield is nil
{% endif %}
```
3. **Use default filter for safety:**
```liquid
{{ product.metafield.value | default: "Not set" }}
```
4. **Enable theme preview console** to see Liquid errors in real-time