If you're running a Shopify store, you've probably encountered situations where your theme's built-in settings don't quite do what you need. Maybe you want to display a custom message based on cart value, show different content for specific products, or create unique layouts that your theme doesn't support.
This is where Liquid comes in. Liquid is Shopify's templating language, and understanding it unlocks the ability to customize virtually every aspect of your store's frontend.
This comprehensive guide will teach you Liquid fundamentals from the ground up. By the end, you'll understand how Liquid works, recognize its core components, and be able to make common customizations yourself.
What Is Shopify Liquid?
Liquid is an open-source templating language created by Shopify. It's the bridge between your store's data (products, collections, customer information) and what visitors see on your website.
Every Shopify theme is built with Liquid. When a customer visits your store, Shopify's servers process the Liquid code, replacing it with actual data before sending the final HTML to the browser. This happens on every page load, ensuring content is always current and dynamic.
Why Liquid Matters for Store Owners
Understanding Liquid gives you several advantages:
- Customization freedom: Go beyond theme settings to create exactly what you envision
- Cost savings: Make changes yourself instead of hiring developers for every tweak
- Faster iterations: Test ideas quickly without waiting for external help
- Better problem-solving: Debug display issues and understand how your theme works
- Competitive edge: Create unique experiences competitors can't easily replicate
Even if you never write complex code, knowing Liquid basics helps you communicate with developers and understand what's possible.
The Three Building Blocks of Liquid
Liquid consists of three fundamental components: objects, tags, and filters. Understanding these is essential before diving into specific examples.
Objects: Outputting Data
Objects contain the data you want to display. They're wrapped in double curly braces {{ }} and tell Liquid to output content.
{{ product.title }}
{{ cart.total_price }}
{{ shop.name }}
When Shopify processes these, it replaces them with actual values:
Wireless Bluetooth Headphones
$149.99
My Awesome Store
Objects can access properties using dot notation. For example, product.title accesses the title property of the product object.
Tags: Controlling Logic
Tags create logic in your templates. They're wrapped in curly braces with percent signs {% %} and don't output anything directly—they control what gets displayed and how.
{% if product.available %}
<button>Add to Cart</button>
{% else %}
<button disabled>Sold Out</button>
{% endif %}
Common tag types include:
- Control flow:
if,elsif,else,unless,case - Iteration:
for,cycle,tablerow - Variable:
assign,capture,increment - Theme:
section,render,layout
Filters: Modifying Output
Filters modify the output of objects or variables. They're applied using the pipe symbol | and can be chained together.
{{ product.title | upcase }}
{{ product.price | money }}
{{ product.description | truncate: 100 }}
Filters transform data before it's displayed:
WIRELESS BLUETOOTH HEADPHONES
$149.99
This premium headphone delivers exceptional sound quality with advanced noise cancellation...
Multiple filters can be chained:
{{ product.title | downcase | replace: ' ', '-' }}
This would output: wireless-bluetooth-headphones
Essential Liquid Objects
Shopify provides numerous objects for accessing store data. Here are the most commonly used:
The Product Object
Access product information anywhere a product is available:
{{ product.title }} // Product name
{{ product.price }} // Price in cents
{{ product.price | money }} // Formatted price ($149.99)
{{ product.description }} // Full description
{{ product.vendor }} // Brand/vendor name
{{ product.type }} // Product type
{{ product.tags }} // Array of tags
{{ product.available }} // Boolean: in stock?
{{ product.images }} // Array of images
{{ product.variants }} // Array of variants
The Collection Object
Work with product collections:
{{ collection.title }}
{{ collection.description }}
{{ collection.products_count }}
{{ collection.products }} // Array of products
{{ collection.image }}
The Cart Object
Access shopping cart data:
{{ cart.item_count }}
{{ cart.total_price | money }}
{{ cart.items }} // Array of line items
{{ cart.requires_shipping }}
{{ cart.note }}
The Shop Object
Get store-wide information:
{{ shop.name }}
{{ shop.email }}
{{ shop.domain }}
{{ shop.currency }}
{{ shop.money_format }}
The Customer Object
Access logged-in customer data:
{% if customer %}
Welcome back, {{ customer.first_name }}!
You have {{ customer.orders_count }} orders.
{% else %}
Please log in for a personalized experience.
{% endif %}
Control Flow: Conditional Logic
Conditionals let you display different content based on specific conditions.
Basic If Statements
{% if product.available %}
<span class="in-stock">In Stock</span>
{% endif %}
If/Else Statements
{% if product.compare_at_price > product.price %}
<span class="sale-badge">Sale!</span>
<span class="original-price">{{ product.compare_at_price | money }}</span>
<span class="sale-price">{{ product.price | money }}</span>
{% else %}
<span class="price">{{ product.price | money }}</span>
{% endif %}
Multiple Conditions with Elsif
{% if cart.total_price >= 10000 %}
<p>You qualify for free shipping!</p>
{% elsif cart.total_price >= 5000 %}
<p>Add {{ 10000 | minus: cart.total_price | money }} more for free shipping.</p>
{% else %}
<p>Free shipping on orders over $100.</p>
{% endif %}
Comparison Operators
Liquid supports these operators:
==equals!=not equal>greater than<less than>=greater than or equal<=less than or equalcontainschecks if string contains substring or array contains item
{% if product.tags contains 'new-arrival' %}
<span class="badge">New!</span>
{% endif %}
{% if product.vendor == 'Premium Brand' %}
<span class="badge">Premium</span>
{% endif %}
Logical Operators
Combine conditions with and and or:
{% if product.available and product.price < 5000 %}
<span class="affordable-and-available">Great Deal!</span>
{% endif %}
{% if customer.tags contains 'vip' or customer.orders_count > 10 %}
<span class="loyalty-discount">You get 10% off!</span>
{% endif %}
The Unless Tag
unless is the opposite of if—it executes when the condition is false:
{% unless product.available %}
<span class="sold-out">Sold Out</span>
{% endunless %}
This is equivalent to:
{% if product.available == false %}
<span class="sold-out">Sold Out</span>
{% endif %}
Loops: Iterating Through Data
Loops let you repeat code for each item in an array.
Basic For Loops
{% for product in collection.products %}
<div class="product-card">
<img src="{{ product.featured_image | img_url: 'medium' }}" alt="{{ product.title }}">
<h3>{{ product.title }}</h3>
<p>{{ product.price | money }}</p>
</div>
{% endfor %}
Limiting Loop Iterations
Control how many items to display:
{% for product in collection.products limit: 4 %}
// Displays first 4 products
{% endfor %}
{% for product in collection.products offset: 4 limit: 4 %}
// Skips first 4, then shows next 4
{% endfor %}
Loop Variables
Liquid provides special variables inside loops:
{% for product in collection.products %}
{% if forloop.first %}
<div class="featured">
{% endif %}
<p>{{ forloop.index }}. {{ product.title }}</p>
{% if forloop.last %}
</div>
{% endif %}
{% endfor %}
Available loop variables:
forloop.index- Current iteration (1-based)forloop.index0- Current iteration (0-based)forloop.first- True on first iterationforloop.last- True on last iterationforloop.length- Total number of iterations
The Else Clause in Loops
Handle empty arrays gracefully:
{% for item in cart.items %}
<div class="cart-item">{{ item.title }}</div>
{% else %}
<p>Your cart is empty.</p>
{% endfor %}
Essential Filters
Filters are crucial for formatting and manipulating data. Here are the most useful ones:
Money Filters
{{ product.price | money }} // $149.99
{{ product.price | money_with_currency }} // $149.99 USD
{{ product.price | money_without_currency }} // 149.99
String Filters
{{ product.title | upcase }} // WIRELESS HEADPHONES
{{ product.title | downcase }} // wireless headphones
{{ product.title | capitalize }} // Wireless headphones
{{ product.title | truncate: 20 }} // Wireless Headpho...
{{ product.title | strip }} // Removes whitespace
{{ product.title | escape }} // HTML-safe output
{{ "hello world" | split: ' ' }} // ["hello", "world"]
{{ product.handle | replace: '-', ' ' }} // Replaces dashes with spaces
Number Filters
{{ 4.5 | ceil }} // 5
{{ 4.5 | floor }} // 4
{{ 4.5 | round }} // 5
{{ 100 | plus: 50 }} // 150
{{ 100 | minus: 25 }} // 75
{{ 100 | times: 2 }} // 200
{{ 100 | divided_by: 4 }} // 25
Array Filters
{{ collection.products | size }} // Number of products
{{ collection.products | first }} // First product
{{ collection.products | last }} // Last product
{{ product.tags | join: ', ' }} // "tag1, tag2, tag3"
{{ collection.products | sort: 'title' }} // Sort by title
Date Filters
{{ article.published_at | date: '%B %d, %Y' }} // February 21, 2026
{{ 'now' | date: '%Y' }} // Current year
Image Filters
{{ product.featured_image | img_url: 'medium' }}
{{ product.featured_image | img_url: '300x300' }}
{{ product.featured_image | img_url: 'master' }}
{{ product.featured_image | image_tag }}
Image size options include: pico, icon, thumb, small, compact, medium, large, grande, original, master, or specific dimensions like 100x100.
Variables: Storing and Reusing Data
Variables let you store values for reuse throughout your template.
Assign
Create simple variables:
{% assign discount_threshold = 10000 %}
{% assign free_shipping_message = 'Free shipping on orders over $100!' %}
{% if cart.total_price >= discount_threshold %}
<p>{{ free_shipping_message }}</p>
{% endif %}
Capture
Store complex content including Liquid output:
{% capture full_price %}
{{ product.compare_at_price | money }}
{% endcapture %}
{% capture sale_price %}
{{ product.price | money }}
{% endcapture %}
{% if product.compare_at_price > product.price %}
<p>Was {{ full_price }}, now {{ sale_price }}!</p>
{% endif %}
Common Customizations: Practical Examples
Now let's apply what you've learned to real-world customizations.
Display Sale Badges
Show a sale badge when products are discounted:
{% if product.compare_at_price > product.price %}
{% assign discount = product.compare_at_price | minus: product.price %}
{% assign discount_percent = discount | times: 100 | divided_by: product.compare_at_price %}
<span class="sale-badge">Save {{ discount_percent }}%</span>
{% endif %}
Free Shipping Progress Bar
Show customers how close they are to free shipping:
{% assign shipping_threshold = 10000 %}
{% assign remaining = shipping_threshold | minus: cart.total_price %}
{% if cart.total_price >= shipping_threshold %}
<div class="shipping-bar complete">
<p>You've earned free shipping!</p>
</div>
{% else %}
{% assign progress = cart.total_price | times: 100 | divided_by: shipping_threshold %}
<div class="shipping-bar">
<div class="progress" style="width: {{ progress }}%"></div>
<p>Add {{ remaining | money }} more for free shipping</p>
</div>
{% endif %}
Product Variant Dropdown
Create a custom variant selector:
{% if product.variants.size > 1 %}
<select name="id" id="variant-selector">
{% for variant in product.variants %}
<option
value="{{ variant.id }}"
{% unless variant.available %}disabled{% endunless %}
>
{{ variant.title }} - {{ variant.price | money }}
{% unless variant.available %} (Sold Out){% endunless %}
</option>
{% endfor %}
</select>
{% else %}
<input type="hidden" name="id" value="{{ product.variants.first.id }}">
{% endif %}
Low Stock Warning
Alert customers when inventory is running low:
{% if product.available %}
{% assign variant = product.selected_or_first_available_variant %}
{% if variant.inventory_quantity <= 5 and variant.inventory_quantity > 0 %}
<p class="low-stock-warning">
Only {{ variant.inventory_quantity }} left in stock!
</p>
{% endif %}
{% endif %}
Personalized Greetings
Welcome returning customers by name:
{% if customer %}
<div class="welcome-message">
<p>Welcome back, {{ customer.first_name }}!</p>
{% if customer.orders_count > 0 %}
<p>Thanks for being a loyal customer.</p>
{% endif %}
</div>
{% else %}
<div class="welcome-message">
<p>Welcome! <a href="/account/login">Sign in</a> for a personalized experience.</p>
</div>
{% endif %}
Related Products by Tag
Display products with matching tags:
{% assign current_tags = product.tags %}
{% assign related_products = collections.all.products | where: 'available', true %}
<div class="related-products">
<h3>You Might Also Like</h3>
<div class="product-grid">
{% assign count = 0 %}
{% for related in related_products %}
{% if related.id != product.id %}
{% for tag in related.tags %}
{% if current_tags contains tag %}
{% if count < 4 %}
<div class="product-card">
<a href="{{ related.url }}">
<img src="{{ related.featured_image | img_url: 'medium' }}">
<h4>{{ related.title }}</h4>
<p>{{ related.price | money }}</p>
</a>
</div>
{% assign count = count | plus: 1 %}
{% endif %}
{% break %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
</div>
</div>
Conditional Content by Product Type
Show different content based on product categories:
{% case product.type %}
{% when 'Clothing' %}
<div class="size-guide">
<a href="/pages/size-guide">Size Guide</a>
</div>
{% when 'Electronics' %}
<div class="warranty-info">
<p>2-year manufacturer warranty included</p>
</div>
{% when 'Food' %}
<div class="nutrition-info">
<a href="#nutrition">View Nutrition Facts</a>
</div>
{% else %}
<div class="return-policy">
<p>30-day hassle-free returns</p>
</div>
{% endcase %}
Working with Snippets and Sections
Liquid templates are organized into reusable pieces.
Snippets
Snippets are reusable code fragments stored in the snippets folder. Include them with the render tag:
{% render 'product-card', product: product %}
Create a snippet called product-card.liquid:
<div class="product-card">
<a href="{{ product.url }}">
<img src="{{ product.featured_image | img_url: 'medium' }}" alt="{{ product.title }}">
<h3>{{ product.title }}</h3>
<p>{{ product.price | money }}</p>
</a>
</div>
Sections
Sections are larger, configurable theme components with schema definitions:
{% section 'featured-collection' %}
Sections include a {% schema %} block defining customizable settings that appear in the theme editor.
Debugging Liquid
When things don't work as expected, these techniques help:
Output Variables for Inspection
<pre>{{ product | json }}</pre>
This outputs the entire object as JSON, showing all available properties.
Check Variable Types
{{ variable | class }}
This shows the data type (string, array, hash, etc.).
Comment Out Code
{% comment %}
This code won't execute
{{ product.title }}
{% endcomment %}
Use Explicit Conditionals
{% if product.title %}
<p>Title exists: {{ product.title }}</p>
{% else %}
<p>Title is empty or doesn't exist</p>
{% endif %}
Best Practices for Liquid Development
Follow these guidelines for clean, maintainable code:
1. Use Meaningful Variable Names
{% comment %} Bad {% endcomment %}
{% assign x = cart.total_price %}
{% comment %} Good {% endcomment %}
{% assign cart_total = cart.total_price %}
2. Keep Logic Simple
Break complex conditions into variables:
{% comment %} Hard to read {% endcomment %}
{% if product.available and product.price < 5000 and product.tags contains 'sale' and customer %}
{% comment %} Better {% endcomment %}
{% assign is_affordable = product.price < 5000 %}
{% assign is_on_sale = product.tags contains 'sale' %}
{% assign eligible = product.available and is_affordable and is_on_sale and customer %}
{% if eligible %}
3. Use Snippets for Reusable Code
Don't repeat yourself. Extract repeated patterns into snippets.
4. Comment Complex Logic
{% comment %}
Calculate discount percentage
Note: prices are in cents, so 10000 = $100
{% endcomment %}
{% assign discount_amount = product.compare_at_price | minus: product.price %}
{% assign discount_percent = discount_amount | times: 100 | divided_by: product.compare_at_price %}
5. Test Edge Cases
Always consider:
- Empty arrays (no products, empty cart)
- Missing data (no image, no description)
- Different user states (logged in vs. guest)
Taking Your Skills Further
Once you've mastered these basics, explore:
- Metafields: Custom data attached to products, collections, customers
- AJAX Cart: Update cart without page reload using JavaScript with Liquid
- Theme architecture: Understanding layouts, templates, and sections
- Shopify CLI: Local development with better tooling
- Theme app extensions: Building installable theme components
Building Your Shopify Store
Learning Liquid is an investment that pays dividends throughout your e-commerce journey. Start with simple customizations, gradually tackle more complex features, and soon you'll have the skills to create exactly the store you envision.
If you're just starting your Shopify journey, take advantage of their free trial to practice these Liquid techniques on a real store. There's no better way to learn than hands-on experimentation.
Remember: every expert was once a beginner. The developers who build impressive Shopify themes started exactly where you are now. With practice and persistence, you'll be customizing themes with confidence in no time.
Whether you're making small tweaks or building entirely custom experiences, Liquid gives you the power to bring your vision to life. Start with the basics covered here, experiment often, and don't be afraid to break things—that's how learning happens.
Ready to start building? Create your Shopify store today and put your new Liquid skills to work.
Looking to improve your e-commerce visibility beyond just theme customization? Get a free AI visibility audit to see how AI assistants recommend products in your category.