Shopify Liquid Templating
Expert guidance for Shopify's Liquid templating language including complete syntax reference, filters, objects, and best practices.
When to Use This Skill
Invoke this skill when:
-
Working with .liquid , .css.liquid , or .js.liquid files
-
Creating or modifying theme templates (product, collection, cart, etc.)
-
Implementing dynamic content rendering
-
Using Liquid filters to format data (money, dates, strings)
-
Accessing Shopify objects (product, collection, cart, customer)
-
Writing conditional logic or loops in templates
-
Debugging Liquid syntax errors or output issues
-
Creating sections or snippets with Liquid logic
-
Formatting prices, dates, or other data
Core Capabilities
- Liquid Syntax Fundamentals
Three core syntax types:
Output (display values):
{{ product.title }} {{ product.price | money }} {{ collection.products.size }}
Logic (conditionals and control):
{% if product.available %} <button>Add to Cart</button> {% else %} <p>Sold Out</p> {% endif %}
Assignment (variables):
{% assign sale_price = product.price | times: 0.8 %} {% capture full_title %}{{ collection.title }} - {{ product.title }}{% endcapture %}
Whitespace control:
{%- if condition -%} Content (strips whitespace) {%- endif -%}
- Control Flow Tags
Conditionals:
-
if/elsif/else/endif
-
Standard conditionals
-
unless/endunless
-
Negated if
-
case/when/else/endcase
-
Switch statements
Logical operators:
-
and , or
-
Combine conditions
-
== , != , > , < , >= , <=
-
Comparisons
-
contains
-
Substring/array search
Example:
{% if product.available and product.price < 100 %} Affordable and in stock {% elsif product.available %} Available but pricey {% else %} Out of stock {% endif %}
- Iteration (Loops)
for loop:
{% for product in collection.products %} {{ product.title }} {% endfor %}
{# With modifiers #} {% for product in collection.products limit: 5 offset: 10 reversed %} {{ product.title }} {% endfor %}
forloop object:
{% for item in array %} {{ forloop.index }} {# 1-based index #} {{ forloop.index0 }} {# 0-based index #} {{ forloop.first }} {# Boolean: first item #} {{ forloop.last }} {# Boolean: last item #} {{ forloop.length }} {# Total items #} {% endfor %}
Pagination:
{% paginate collection.products by 12 %} {% for product in paginate.collection.products %} {% render 'product-card', product: product %} {% endfor %}
{% if paginate.pages > 1 %} {{ paginate | default_pagination }} {% endif %} {% endpaginate %}
- Essential Filters
Money formatting:
{{ 1000 | money }} {# $10.00 #} {{ 1000 | money_without_currency }} {# 10.00 #} {{ 1000 | money_without_trailing_zeros }} {# $10 #}
String manipulation:
{{ "hello" | upcase }} {# HELLO #} {{ "hello" | capitalize }} {# Hello #} {{ "hello world" | truncate: 8 }} {# hello... #} {{ "a,b,c" | split: "," }} {# ["a","b","c"] #} {{ text | strip_html }} {# Remove HTML tags #}
Array/collection:
{{ array | first }} {# First element #} {{ array | last }} {# Last element #} {{ array | size }} {# Count #} {{ products | map: "title" }} {# Extract property #} {{ products | where: "vendor", "Nike" }} {# Filter #} {{ products | sort: "price" }} {# Sort #} {{ array | join: ", " }} {# Join with separator #}
Date formatting:
{{ order.created_at | date: '%B %d, %Y' }} {# November 10, 2025 #} {{ order.created_at | date: '%m/%d/%Y' }} {# 11/10/2025 #} {{ order.created_at | date: '%H:%M %p' }} {# 12:39 PM #}
Image handling:
{{ product.image | img_url: '500x500' }} {# Resize image #} {{ product.image | img_url: 'medium' }} {# Named size #} {{ 'logo.png' | asset_url }} {# Theme asset CDN #}
Math operations:
{{ 5 | plus: 3 }} {# 8 #} {{ 5 | minus: 3 }} {# 2 #} {{ 5 | times: 3 }} {# 15 #} {{ 10 | divided_by: 2 }} {# 5 #} {{ 1.567 | round: 2 }} {# 1.57 #}
Chaining filters:
{{ collection.products | where: "available" | map: "title" | sort | first }}
- Key Shopify Objects
Product object:
{{ product.title }} {{ product.price | money }} {{ product.available }} {# Boolean #} {{ product.vendor }} {{ product.type }} {{ product.images }} {# Array #} {{ product.variants }} {# Array #} {{ product.selected_variant }} {{ product.metafields.custom.field }}
Collection object:
{{ collection.title }} {{ collection.products }} {# Array #} {{ collection.products_count }} {{ collection.all_tags }} {{ collection.sort_by }} {{ collection.filters }}
Cart object (global):
{{ cart.item_count }} {{ cart.total_price | money }} {{ cart.items }} {# Array of line items #} {{ cart.empty? }} {# Boolean #}
Customer object:
{{ customer.name }} {{ customer.email }} {{ customer.orders_count }} {{ customer.total_spent | money }} {{ customer.default_address }}
Global objects:
{{ shop.name }} {{ shop.currency }} {{ shop.url }} {{ request.path }} {{ request.page_type }} {# "product", "collection", etc. #} {{ settings.color_primary }} {# Theme settings #}
- Template Inclusion
render (isolated scope - PREFERRED):
{% render 'product-card', product: product, show_price: true %}
{# Render for each item #} {% render 'product-card' for collection.products as item %}
include (shared scope - LEGACY):
{% include 'product-details' %}
section (dynamic sections):
{% section 'featured-product' %}
Common Patterns
Product availability check
{% if product.available %} <button type="submit">Add to Cart</button> {% elsif product.selected_variant.incoming %} <p>Coming {{ product.selected_variant.incoming_date | date: '%B %d' }}</p> {% else %} <p class="sold-out">Sold Out</p> {% endif %}
Price display with sale
{% if product.compare_at_price > product.price %} <span class="sale-price">{{ product.price | money }}</span> <span class="original-price">{{ product.compare_at_price | money }}</span> <span class="savings">Save {{ product.compare_at_price | minus: product.price | money }}</span> {% else %} <span class="price">{{ product.price | money }}</span> {% endif %}
Loop through variants
{% for variant in product.variants %} <option value="{{ variant.id }}" {% unless variant.available %}disabled{% endunless %}
{{ variant.title }} - {{ variant.price | money }}
</option> {% endfor %}
Check collection tags
{% if collection.all_tags contains 'sale' %} <div class="sale-banner">Sale items available!</div> {% endif %}
Best Practices
-
Use whitespace control ({%- and -%} ) to keep HTML clean
-
Prefer render over include for better performance and isolation
-
Cache expensive operations by assigning to variables
-
Use descriptive variable names for clarity
-
Leverage filters instead of complex logic when possible
-
Check for existence before accessing nested properties
-
Use default filter for fallback values: {{ product.metafield | default: "N/A" }}
Detailed References
For comprehensive documentation:
-
references/syntax.md - Complete syntax reference with all tags
-
references/filters.md - All 60+ filters with examples
-
references/objects.md - Complete object property reference
Integration with Other Skills
-
shopify-theme-dev - Use when working with theme file structure and sections
-
shopify-api - Use when fetching data via Ajax or GraphQL to display in Liquid
-
shopify-debugging - Use when troubleshooting Liquid rendering issues
-
shopify-performance - Use when optimizing Liquid template performance
Quick Syntax Reference
{# Output #} {{ variable }} {{ product.title | upcase }}
{# Conditionals #} {% if condition %}...{% elsif %}...{% else %}...{% endif %} {% unless condition %}...{% endunless %} {% case variable %}{% when value %}...{% endcase %}
{# Loops #} {% for item in array %}...{% endfor %} {% for item in array limit: 5 offset: 10 %}...{% endfor %} {% break %} / {% continue %}
{# Variables #} {% assign var = value %} {% capture var %}content{% endcapture %}
{# Inclusion #} {% render 'snippet', param: value %} {% section 'section-name' %}
{# Comments #} {% comment %}...{% endcomment %} {# Single line #}