liquid-templating
Shopify Liquid Templating
When to use this skill
Use this skill when:
- Writing Liquid template code
- Accessing Shopify data (products, collections, cart, etc.)
- Using Liquid filters to transform output
- Creating conditional logic and loops
- Building dynamic theme content
- Debugging Liquid code issues
- Optimizing Liquid performance
Liquid Basics
Output Tags
Output data using double curly braces:
{{ product.title }}
{{ shop.name }}
{{ 'hello' | upcase }}
Logic Tags
Use logic with {% %} tags:
{% if product.available %}
<p>In Stock</p>
{% else %}
<p>Sold Out</p>
{% endif %}
Whitespace Control
Use - to strip whitespace:
{%- if condition -%}
content
{%- endif -%}
Core Objects
Product Object
{{ product.title }}
{{ product.description }}
{{ product.price | money }}
{{ product.compare_at_price | money }}
{{ product.vendor }}
{{ product.type }}
{{ product.tags | join: ', ' }}
{{ product.available }}
{{ product.url }}
<!-- Featured Image -->
{{ product.featured_image | image_url: width: 500 | image_tag }}
<!-- All Images -->
{% for image in product.images %}
{{ image | image_url: width: 300 | image_tag }}
{% endfor %}
<!-- Variants -->
{% for variant in product.variants %}
{{ variant.title }} - {{ variant.price | money }}
{% endfor %}
<!-- Metafields -->
{{ product.metafields.custom.care_instructions }}
Collection Object
{{ collection.title }}
{{ collection.description }}
{{ collection.products_count }}
{{ collection.url }}
{% for product in collection.products %}
{{ product.title }}
{% endfor %}
<!-- Pagination -->
{% paginate collection.products by 12 %}
{% for product in collection.products %}
{% render 'product-card', product: product %}
{% endfor %}
{{ paginate | default_pagination }}
{% endpaginate %}
Cart Object
{{ cart.item_count }}
{{ cart.total_price | money }}
{{ cart.total_weight | weight_with_unit }}
{% for item in cart.items %}
{{ item.product.title }}
{{ item.variant.title }}
{{ item.quantity }}
{{ item.line_price | money }}
{% endfor %}
<!-- Cart Attributes -->
{{ cart.attributes.gift_message }}
<!-- Cart Note -->
{{ cart.note }}
Customer Object
{% if customer %}
Hello, {{ customer.first_name }}!
{{ customer.email }}
{{ customer.orders_count }} orders
{{ customer.total_spent | money }}
{% for address in customer.addresses %}
{{ address.street }}
{{ address.city }}, {{ address.province }}
{% endfor %}
{% else %}
<a href="/account/login">Log in</a>
{% endif %}
Shop Object
{{ shop.name }}
{{ shop.email }}
{{ shop.domain }}
{{ shop.money_format }}
{{ shop.currency }}
{{ shop.enabled_currencies }}
Request Object
{{ request.locale.iso_code }}
{{ request.page_type }}
{{ request.path }}
{{ request.host }}
Essential Filters
String Filters
{{ 'hello world' | capitalize }} <!-- Hello world -->
{{ 'hello world' | upcase }} <!-- HELLO WORLD -->
{{ 'HELLO' | downcase }} <!-- hello -->
{{ ' hello ' | strip }} <!-- hello -->
{{ 'hello' | append: ' world' }} <!-- hello world -->
{{ 'hello world' | prepend: 'say ' }} <!-- say hello world -->
{{ 'hello' | replace: 'e', 'a' }} <!-- hallo -->
{{ 'hello world' | split: ' ' }} <!-- ['hello', 'world'] -->
{{ 'hello world' | truncate: 8 }} <!-- hello... -->
{{ 'hello world' | truncatewords: 1 }} <!-- hello... -->
{{ 'hello' | size }} <!-- 5 -->
Number Filters
{{ 4.5 | ceil }} <!-- 5 -->
{{ 4.5 | floor }} <!-- 4 -->
{{ 4.567 | round: 2 }} <!-- 4.57 -->
{{ 5 | plus: 3 }} <!-- 8 -->
{{ 5 | minus: 3 }} <!-- 2 -->
{{ 5 | times: 3 }} <!-- 15 -->
{{ 10 | divided_by: 3 }} <!-- 3 -->
{{ 10 | modulo: 3 }} <!-- 1 -->
{{ 1234.56 | money }} <!-- $1,234.56 -->
{{ 1234.56 | money_without_currency }} <!-- 1,234.56 -->
Array Filters
{{ array | first }}
{{ array | last }}
{{ array | size }}
{{ array | join: ', ' }}
{{ array | sort }}
{{ array | sort: 'price' }}
{{ array | reverse }}
{{ array | uniq }}
{{ array | compact }}
{{ array | concat: other_array }}
{{ array | map: 'title' }}
{{ array | where: 'available', true }}
URL Filters
{{ 'products' | url }}
{{ product.url | within: collection }}
{{ 'style.css' | asset_url }}
{{ 'image.png' | asset_url }}
{{ 'logo.png' | file_url }}
{{ product.featured_image | image_url: width: 500 }}
{{ product | product_image_url: 'master' }}
Image Filters
<!-- Modern image_url approach (recommended) -->
{{ image | image_url: width: 800 }}
{{ image | image_url: width: 800, height: 600, crop: 'center' }}
{{ image | image_url: width: 800 | image_tag }}
<!-- Responsive images -->
{{ image | image_url: width: 1200 | image_tag:
loading: 'lazy',
widths: '300, 600, 900, 1200',
sizes: '(max-width: 600px) 100vw, 50vw'
}}
<!-- With alt text -->
{{ image | image_url: width: 500 | image_tag: alt: product.title }}
Money Filters
{{ product.price | money }} <!-- $10.00 -->
{{ product.price | money_with_currency }} <!-- $10.00 USD -->
{{ product.price | money_without_trailing_zeros }} <!-- $10 -->
{{ product.price | money_without_currency }} <!-- 10.00 -->
Date Filters
{{ article.created_at | date: '%B %d, %Y' }} <!-- January 15, 2025 -->
{{ article.created_at | date: '%Y-%m-%d' }} <!-- 2025-01-15 -->
{{ 'now' | date: '%H:%M' }} <!-- 14:30 -->
Control Flow
Conditionals
{% if product.available %}
In Stock
{% elsif product.compare_at_price %}
Sale
{% else %}
Sold Out
{% endif %}
<!-- Unless (opposite of if) -->
{% unless product.available %}
Sold Out
{% endunless %}
<!-- Case/When -->
{% case product.type %}
{% when 'Shirt' %}
<p>It's a shirt!</p>
{% when 'Pants' %}
<p>It's pants!</p>
{% else %}
<p>Unknown type</p>
{% endcase %}
Comparison Operators
{% if product.price > 1000 %}{% endif %}
{% if product.price >= 1000 %}{% endif %}
{% if product.price < 1000 %}{% endif %}
{% if product.price <= 1000 %}{% endif %}
{% if product.title == 'Hat' %}{% endif %}
{% if product.title != 'Hat' %}{% endif %}
{% if product.tags contains 'sale' %}{% endif %}
{% if product.title %}{% endif %} <!-- truthy check -->
{% if product.title == blank %}{% endif %} <!-- blank check -->
Logical Operators
{% if product.available and product.price < 5000 %}
Affordable and in stock!
{% endif %}
{% if product.type == 'Shirt' or product.type == 'Pants' %}
It's clothing!
{% endif %}
Loops
For Loop
{% for product in collection.products %}
{{ forloop.index }}: {{ product.title }}
{% endfor %}
<!-- With limit and offset -->
{% for product in collection.products limit: 4 offset: 2 %}
{{ product.title }}
{% endfor %}
<!-- Reversed -->
{% for product in collection.products reversed %}
{{ product.title }}
{% endfor %}
<!-- Forloop variables -->
{% for item in array %}
{{ forloop.index }} <!-- 1, 2, 3... -->
{{ forloop.index0 }} <!-- 0, 1, 2... -->
{{ forloop.first }} <!-- true on first -->
{{ forloop.last }} <!-- true on last -->
{{ forloop.length }} <!-- total items -->
{% endfor %}
<!-- Else for empty -->
{% for product in collection.products %}
{{ product.title }}
{% else %}
No products found.
{% endfor %}
Cycle
{% for product in collection.products %}
<div class="{% cycle 'odd', 'even' %}">
{{ product.title }}
</div>
{% endfor %}
Tablerow
<table>
{% tablerow product in collection.products cols: 3 %}
{{ product.title }}
{% endtablerow %}
</table>
Variables
Assign
{% assign my_variable = 'Hello' %}
{% assign price_in_dollars = product.price | divided_by: 100.0 %}
Capture
{% capture full_name %}
{{ customer.first_name }} {{ customer.last_name }}
{% endcapture %}
<p>Hello, {{ full_name }}!</p>
Increment/Decrement
{% increment counter %} <!-- 0 -->
{% increment counter %} <!-- 1 -->
{% decrement counter %} <!-- -1 -->
Snippets and Sections
Render Snippets
<!-- Basic render -->
{% render 'product-card' %}
<!-- With variables -->
{% render 'product-card', product: product %}
<!-- With multiple variables -->
{% render 'product-card', product: product, show_price: true %}
<!-- For loop with render -->
{% render 'product-card' for collection.products as product %}
Section Tags
<!-- In layout file -->
{% section 'header' %}
{% sections 'footer-group' %}
<!-- Content placeholder -->
{{ content_for_layout }}
{{ content_for_header }}
Forms
Product Form
{% form 'product', product %}
<select name="id">
{% for variant in product.variants %}
<option value="{{ variant.id }}">{{ variant.title }}</option>
{% endfor %}
</select>
<input type="number" name="quantity" value="1" min="1">
<button type="submit">Add to Cart</button>
{% endform %}
Contact Form
{% form 'contact' %}
<input type="email" name="contact[email]" required>
<textarea name="contact[body]"></textarea>
<button type="submit">Send</button>
{% endform %}
Customer Forms
<!-- Login -->
{% form 'customer_login' %}
<input type="email" name="customer[email]">
<input type="password" name="customer[password]">
<button type="submit">Log In</button>
{% endform %}
<!-- Register -->
{% form 'create_customer' %}
<input type="text" name="customer[first_name]">
<input type="text" name="customer[last_name]">
<input type="email" name="customer[email]">
<input type="password" name="customer[password]">
<button type="submit">Create Account</button>
{% endform %}
Best Practices
- Use
rendernotinclude-renderis faster and scopes variables - Avoid complex logic in Liquid - Move to JavaScript when possible
- Use descriptive variable names -
{% assign product_price = ... %} - Leverage caching - Use Liquid responsibly within sections
- Handle blank states - Always check for
blankor empty arrays - Use schema defaults - Provide sensible defaults in section schemas
Common Patterns
Sale Badge
{% if product.compare_at_price > product.price %}
{% assign savings = product.compare_at_price | minus: product.price %}
{% assign percent_off = savings | times: 100.0 | divided_by: product.compare_at_price | round %}
<span class="sale-badge">{{ percent_off }}% OFF</span>
{% endif %}
Variant Selector
{% unless product.has_only_default_variant %}
{% for option in product.options_with_values %}
<label>{{ option.name }}</label>
<select name="options[{{ option.name }}]">
{% for value in option.values %}
<option value="{{ value }}">{{ value }}</option>
{% endfor %}
</select>
{% endfor %}
{% endunless %}
Resources
More from dragnoir/shopify-agent-skills
headless-hydrogen
Build headless Shopify storefronts with Hydrogen and Oxygen. Use this skill for creating custom React-based storefronts, using Hydrogen components, deploying to Oxygen hosting, working with the Storefront API, and building high-performance e-commerce experiences. Also covers bringing your own stack with custom frameworks.
37app-development
Build Shopify apps with extensions and embedded experiences. Use this skill for creating new Shopify apps, adding app extensions, building admin interfaces, working with OAuth authentication, managing app configuration, and deploying to the Shopify App Store. Covers Shopify CLI for apps, Polaris UI, and app bridge.
13api-graphql
Work with Shopify GraphQL APIs including Admin API and Storefront API. Use this skill for querying and mutating Shopify data, managing products, orders, customers, handling pagination, working with metafields, and understanding rate limits. Covers authentication, queries, mutations, and webhooks.
12theme-development
Build, customize, and deploy Shopify themes. Use this skill for creating new themes, modifying existing themes, understanding theme architecture, working with sections/blocks, and optimizing theme performance. Covers Skeleton theme, Dawn theme, layouts, templates, and the theme editor customization experience.
12checkout-customization
Customize Shopify checkout with UI extensions and functions. Use this skill for building checkout UI extensions, adding custom fields, implementing payment customizations, creating post-purchase experiences, and extending customer accounts. Covers Checkout UI Extensions API and checkout branding.
12shopify-functions
Build backend logic with Shopify Functions. Use this skill for creating custom discounts, delivery customization, payment customization, cart and checkout validation, and order routing. Functions run on Shopify's infrastructure using WebAssembly. Supports Rust and JavaScript.
12