Free guide · every tag and filter · about 60 minutes
Liquid explained simply
Every part of Liquid, one by one: what it does, when and why you use it, and a small Shopify example with the real output. No jargon.
What is Liquid?
Liquid is the language Shopify uses to put store data into a page.
Think of a wedding card printer. They have one card design with blank spaces: "Dear ______, you are invited…". For each guest, they fill in the name. The design stays the same. Only the blanks change.
A Shopify theme is that card design. Liquid fills in the blanks: the product name, the price, the photo, the cart total.
How it works
- A shopper opens a page, like a product page.
- Shopify reads the theme file and finds the Liquid code.
- Shopify replaces the Liquid with real data (this product's title, this product's price).
- The shopper's browser gets plain HTML. They never see the Liquid.
How to try every example
- Open Shopify admin → Online Store → Themes → Horizon → Customize.
- Click Add section (+) and pick Custom Liquid.
- Paste any example from this page into the box.
- Look at the preview on the right.
{{ shop.name }} into a Custom Liquid section and the preview shows your store's name.The three building blocks
Every bit of Liquid is one of these three things.
1. Output: double curly braces
Double curly braces print something on the page. Think of them as "show this here".
{{ product.title }}Masala Chai2. Tags: curly brace and percent
Tags are the logic: "if this, do that", "repeat this for every product". They do not print anything by themselves.
{% if product.available %}
In stock
{% endif %}In stock3. Filters: the pipe sign |
A filter changes a value before it is printed. Think of a photo filter: same photo, new look. You add it with the pipe sign |.
{{ product.title | upcase }}MASALA CHAIYou can chain filters. They run from left to right:
{{ product.title | upcase | append: " - SALE" }}MASALA CHAI - SALEBasics
Types: the kinds of values
Every value in Liquid is one of these types.
| Type | What it is | Example |
|---|---|---|
| String | Text, inside quotes | "Hello" or 'Hello' |
| Number | Whole or decimal numbers | 25 or -39.75 |
| Boolean | Yes or no | true or false |
| Nil | "Nothing here" | a field with no value |
| Array | A list of things | product.tags, collection.products |
| EmptyDrop | An object that does not exist | a page handle that is wrong |
{% assign name = "Chai" %}
{% assign price = 250 %}
{% assign on_sale = true %}
{{ name }} costs {{ price }}. On sale: {{ on_sale }}Chai costs 250. On sale: trueArrays (lists). You read one item by its position in square brackets. Counting starts at 0. A negative number counts from the end.
{{ product.tags[0] }}
{{ product.tags[-1] }}organic
bestsellerYou cannot type a list directly in Liquid. To make one, use the split filter (see Filters below).
Nil prints nothing. If a value does not exist, Liquid prints an empty space, not an error.
Hello {{ customer.first_name }}!Hello !EmptyDrop and empty. To check if something exists, or if a list or text is empty, compare it with empty:
{% if product.tags == empty %}
This product has no tags.
{% endif %}Operators: comparing values
Operators let you ask questions inside if and unless.
| Operator | Meaning | Example |
|---|---|---|
== | is equal to | product.type == "Tea" |
!= | is not equal to | product.type != "Tea" |
> | greater than | product.price > 50000 |
< | less than | cart.item_count < 3 |
>= | greater than or equal | product.price >= 100000 |
<= | less than or equal | variant.inventory_quantity <= 5 |
and | both must be true | a and b |
or | at least one must be true | a or b |
contains | text or list has this | product.tags contains "sale" |
contains works two ways:
{% if product.title contains "Chai" %}
This is a chai product.
{% endif %}
{% if product.tags contains "sale" %}
On sale!
{% endif %}{% if true or false and false %}
This prints, because "false and false" is checked first.
{% endif %}Truthy and falsy: what counts as "yes"
When you write {% if something %}, Liquid must decide: yes or no?
Only two things are "no" (falsy): false and nil. Everything else is "yes" (truthy), even an empty text "" and the number 0.
| Value | Counts as |
|---|---|
true | yes |
false | no |
nil (nothing) | no |
any text, even "" | yes |
0 and any number | yes |
| any list, even empty | yes |
This surprises everyone once. An empty text is still "yes":
{% if product.metafields.custom.subtitle %}
<h2>{{ product.metafields.custom.subtitle }}</h2>
{% endif %}<h2></h2>Whitespace control: removing empty lines
Every Liquid tag leaves an empty line in the HTML, even if it prints nothing. Add a dash - inside the braces to remove the space on that side: {%- and -%}, or {{- and -}}.
Use it when: the HTML looks messy, or extra spaces break your layout (for example spaces inside a price or a class name).
{% assign tea = "Masala Chai" %}
{{ tea }}
Masala Chai{% assign tea = "Masala Chai" -%}
{{ tea }}Masala ChaiHorizon uses {%- and -%} almost everywhere. Now you know why.
Variations: core Liquid vs Shopify Liquid
Liquid is used in many places. Each place adds its own extras.
- Core Liquid (this page): the tags, filters and basics that work everywhere.
- Shopify Liquid: core Liquid plus store objects (
product,cart,collection,customer) and store filters (money,image_url,asset_url,t). Full list: shopify.dev/docs/api/liquid (opens in a new tab). - Jekyll Liquid: used for blogs on GitHub Pages, with its own extras.
Why it matters: if a filter from a Jekyll tutorial does not work in your theme, it is probably Jekyll-only.
Tags: control flow (making decisions)
These tags decide which part of the code runs.
if
Runs the code inside only if the condition is true.
Use it when: something should show only sometimes: a "Sold out" label, a sale badge, a message for logged-in customers.
{% if product.available == false %}
<span class="badge">Sold out</span>
{% endif %}<span class="badge">Sold out</span>unless
The opposite of if: runs the code only if the condition is false. "Unless it is raining, we go out."
Use it when: "show this, except when…" reads more naturally than if … != ….
{% unless product.available %}
Sold out
{% endunless %}This is the same as {% if product.available == false %}. Use whichever is easier to read.
elsif and else
Add more choices to an if. elsif = "otherwise, if…". else = "in every other case".
Use it when: there are 3 or more possible results, like stock levels.
{% assign qty = product.selected_or_first_available_variant.inventory_quantity %}
{% if qty <= 0 %}
Sold out
{% elsif qty < 5 %}
Only {{ qty }} left!
{% else %}
In stock
{% endif %}Only 3 left!case and when
Checks one value against a list of choices. Like a lift: press 1, 2 or 3, and you go to that floor.
Use it when: you compare the same thing many times (product type, number of blocks, a setting). It is cleaner than many elsif lines.
{% case product.type %}
{% when "Tea" %}
Brew for 3 minutes.
{% when "Coffee", "Espresso" %}
Brew for 4 minutes.
{% else %}
See the label for instructions.
{% endcase %}Brew for 3 minutes.One when can take several values, separated by commas or or.
Tags: iteration (repeating)
These tags repeat a piece of code, once for each item in a list.
for
Runs the code once for every item.
Use it when: you show many things in the same way: products in a collection, items in the cart, tags on a product.
{% for product in collection.products %}
<p>{{ product.title }}</p>
{% endfor %}<p>Masala Chai</p>
<p>Green Tea</p>
<p>Clay Cup</p>for … else
The else part runs when the list is empty.
Use it when: you want a friendly message instead of a blank space.
{% for item in cart.items %}
{{ item.title }}
{% else %}
Your cart is empty.
{% endfor %}Your cart is empty.break
Stops the loop right away.
Use it when: you found what you were looking for and do not need to check the rest.
{% for i in (1..5) %}
{% if i == 4 %}{% break %}{% endif %}
{{ i }}
{% endfor %}1 2 3continue
Skips the current item and moves to the next one.
Use it when: some items should be left out, like sold-out products in a "featured" row.
{% for product in collection.products %}
{% unless product.available %}{% continue %}{% endunless %}
{{ product.title }}
{% endfor %}Masala Chai Clay Cuplimit and offset
limit = how many items at most. offset = how many to skip at the start.
Use it when: you show "the first 4 products", or "products 5 to 8".
{% for product in collection.products limit: 2 %}
{{ product.title }}
{% endfor %}
{% for product in collection.products offset: 2 %}
{{ product.title }}
{% endfor %}Masala Chai Green Tea
Clay Cupoffset: continue starts where the last loop over the same list stopped. Useful to split one list into two rows.
range
Loops over numbers instead of a list. Write it as (start..end).
Use it when: you need to repeat something a fixed number of times: 5 rating stars, 3 empty placeholder cards.
{% for i in (1..5) %}★{% endfor %}★★★★★The end number can be a variable: (1..rating).
reversed
Loops from the last item to the first.
Use it when: you want newest first, or the opposite order of the list.
{% for i in (1..3) reversed %}{{ i }} {% endfor %}3 2 1Note the spelling: the loop word is reversed, the filter is reverse.
forloop (information about the loop)
Inside every for loop, Liquid gives you a helper called forloop. It knows where you are in the loop.
| Property | What it gives you |
|---|---|
forloop.index | position, starting at 1 |
forloop.index0 | position, starting at 0 |
forloop.first | true on the first item |
forloop.last | true on the last item |
forloop.length | how many items in total |
forloop.rindex | how many are left, counting this one |
forloop.parentloop | the outer loop, when loops are inside loops |
Use it when: the first or last item needs something different, like commas between words but not after the last one.
{% for tag in product.tags %}
{{ tag }}{% unless forloop.last %}, {% endunless %}
{% endfor %}organic, assam, bestsellercycle
Gives the next value from a list each time it is called, then starts again. Like a traffic light: red, yellow, green, red…
Use it when: you alternate styles, like striped table rows (odd, even) or left/right image positions.
{% for product in collection.products %}
<div class="{% cycle 'row-light', 'row-dark' %}">{{ product.title }}</div>
{% endfor %}<div class="row-light">Masala Chai</div>
<div class="row-dark">Green Tea</div>
<div class="row-light">Clay Cup</div>If you use two separate cycles in one file, give each a name so they do not mix: {% cycle 'colors': 'red', 'blue' %}.
tablerow
Builds the rows and cells of an HTML table for you. You write the <table> tags, it writes the <tr> and <td>.
Use it when: you need a real table, like a size chart or a product comparison. For product grids, use for with CSS grid instead.
<table>
{% tablerow product in collection.products cols: 2 %}
{{ product.title }}
{% endtablerow %}
</table><table>
<tr class="row1"><td class="col1">Masala Chai</td><td class="col2">Green Tea</td></tr>
<tr class="row2"><td class="col1">Clay Cup</td></tr>
</table>tablerow also takes cols, limit, offset and ranges like (1..6). Inside, a helper called tablerowloop works like forloop, plus col, row, col_first and col_last.
Tags: template (how files work together)
comment
Text between {% comment %} and {% endcomment %} is not shown and not run.
Use it when: you leave a note for the next developer, or turn off a piece of code for a while without deleting it.
{% comment %}
Old banner. Turned off for Diwali. Bring it back in January.
{% endcomment %}Inline comment (#)
A shorter comment for one line: start with # inside a tag.
Use it when: you want a quick note on one line.
{% # Show the badge only for sale items %}
{%- if product.tags contains "sale" -%}SALE{%- endif -%}SALEraw
Everything between {% raw %} and {% endraw %} is printed exactly as written, without running it as Liquid.
Use it when: you need to show curly braces on the page, for example JavaScript template code (like Vue or Handlebars) that also uses double curly braces, or a tutorial that shows Liquid code.
{% raw %}Write {{ product.title }} to print the title.{% endraw %}Write {{ product.title }} to print the title.liquid
Lets you write many tags inside one tag, one per line, without repeating {% and %} every time.
Use it when: you have a lot of logic and few outputs. It is much easier to read. Horizon uses it at the top of many blocks.
{% liquid
assign qty = product.selected_or_first_available_variant.inventory_quantity
if qty < 5
assign stock_class = 'stock--low'
else
assign stock_class = 'stock--ok'
endif
%}
<p class="{{ stock_class }}">In stock</p><p class="stock--low">In stock</p>echo
Prints a value, like double curly braces, but it works inside the liquid tag.
Use it when: you are inside {% liquid %} and need to print something.
{% liquid
for product in collection.products limit: 2
echo product.title | upcase
echo ' / '
endfor
%}MASALA CHAI / GREEN TEA /render
Inserts another file (a snippet) from the snippets folder.
Use it when: the same piece of HTML is used in many places, like a product card. Write it once, use it everywhere. Change it once, it changes everywhere.
{% render 'product-card', product: product, show_vendor: true %}Things to know:
- Do not write
.liquidat the end of the name. - The snippet cannot see your variables. You must pass them in, like
product: productabove. This keeps snippets safe and predictable. - with: pass one object under a name:
{% render 'product-card' with featured as product %} - for: render the snippet once per item:
{% render 'product-card' for collection.products as product %}. Inside,forloopworks.
include (old, do not use)
The old way to insert a snippet. It could see and change the parent's variables, which made code slow and confusing. Use render instead. You will still see include in old themes, so it is good to recognise it.
Tags: variables (saving values)
assign
Saves a value under a name so you can use it later. Like writing a phone number on a sticky note.
Use it when: you use the same value many times, or a long expression is hard to read.
{% assign variant = product.selected_or_first_available_variant %}
{% assign is_cheap = false %}
{% if variant.price < 50000 %}{% assign is_cheap = true %}{% endif %}
Under ₹500: {{ is_cheap }}Under ₹500: truePut text in quotes: {% assign label = "New" %}. Numbers, true and false have no quotes.
capture
Saves everything between the tags as text, including HTML and other outputs.
Use it when: you build a longer piece of text or HTML from several parts, then use it later (or more than once).
{% capture share_text %}I just bought {{ product.title }} from {{ shop.name }}!{% endcapture %}
<a href="https://wa.me/?text={{ share_text | url_encode }}">Share on WhatsApp</a><a href="https://wa.me/?text=I+just+bought+Masala+Chai+from+Chai+Corner%21">Share on WhatsApp</a>What you capture is always text, even if it looks like a number.
increment
Makes a counter that starts at 0, prints it, and adds 1 each time.
Use it when: you need unique numbers on the page, like IDs for many accordions: faq-0, faq-1, faq-2.
{% increment counter %}
{% increment counter %}
{% increment counter %}0
1
2The counter is separate from assign. If you assign counter = 10 and then increment counter, you still get 0, 1, 2, and counter stays 10.
decrement
Like increment, but starts at -1 and goes down by 1 each time.
{% decrement countdown %}
{% decrement countdown %}
{% decrement countdown %}-1
-2
-3Filters: text
These filters change text (strings).
append
Adds text to the end.
Use it when: you build a link, a file name or a CSS class from parts.
{{ product.url | append: "?ref=homepage" }}/products/masala-chai?ref=homepageprepend
Adds text to the start.
Use it when: you add a label in front, like "Brand: " or a currency word.
{{ product.vendor | prepend: "Brand: " }}Brand: Chai Cornerupcase
Makes ALL LETTERS CAPITAL.
Use it when: you need shouting text, like a SALE badge.
{{ "sale" | upcase }}SALEdowncase
Makes all letters small.
Use it when: you compare text and do not want capitals to matter, or you build a CSS class from a name.
{{ "Green Tea" | downcase }}green teacapitalize
Makes the first letter capital and the rest small.
Use it when: text comes in messy (all caps or all small) and you want it to look tidy.
{{ "mASALA chai" | capitalize }}Masala chaistrip, lstrip, rstrip
Remove spaces and new lines from the edges. strip = both sides, lstrip = left only, rstrip = right only. Spaces between words stay.
Use it when: a setting or metafield has extra spaces that break your layout or a comparison.
[{{ " Masala Chai " | strip }}]
[{{ " Masala Chai " | lstrip }}]
[{{ " Masala Chai " | rstrip }}][Masala Chai]
[Masala Chai ]
[ Masala Chai]squish
Removes spaces at both ends and turns any group of spaces or new lines inside into one space.
Use it when: you build a class list or style text with capture across many lines, and want it on one clean line.
{{ " card card--sale
card--small " | squish }}card card--sale card--smallstrip_html
Removes all HTML tags, leaving only the text.
Use it when: you need plain text from a description, for example for a meta description or a short preview.
{{ "<p>Strong <strong>Assam</strong> tea.</p>" | strip_html }}Strong Assam tea.strip_newlines
Removes all line breaks.
Use it when: text must be on one line, like inside a JavaScript string or a data attribute.
{% capture note %}
Hello
there
{% endcapture %}
{{ note | strip_newlines }}Hellotherenewline_to_br
Adds an HTML line break <br /> at every new line.
Use it when: the merchant types text with Enter in a plain text box (like an address or a note), and you want those lines to show on the page.
{{ "Shop 12
MG Road
Pune" | newline_to_br }}Shop 12<br />
MG Road<br />
Puneremove, remove_first, remove_last
Delete a piece of text. remove = every time it appears. remove_first = only the first time. remove_last = only the last time.
Use it when: you clean up text, like removing a brand name from titles, or a word like "Default".
{{ "Chai Corner Masala Chai" | remove: "Chai Corner " }}
{{ "tea, tea, tea" | remove_first: "tea, " }}
{{ "tea, tea, tea" | remove_last: ", tea" }}Masala Chai
tea, tea
tea, teareplace, replace_first, replace_last
Swap one piece of text for another. Same "every / first / last" idea as remove.
Use it when: you change words or characters, like turning a handle into a readable name.
{{ "masala-chai-500g" | replace: "-", " " }}
{{ "a-b-c" | replace_first: "-", "+" }}
{{ "a-b-c" | replace_last: "-", "+" }}masala chai 500g
a+b-c
a-b+ctruncate
Cuts text to a number of characters, and adds ... at the end. The ... counts in the number.
Use it when: long titles or descriptions break your card design.
{{ "Organic Assam Masala Chai with Cardamom" | truncate: 20 }}
{{ "Organic Assam Masala Chai with Cardamom" | truncate: 20, " →" }}Organic Assam Mas...
Organic Assam Masa →The second value changes the ending. Use "" for no ending.
truncatewords
Cuts text to a number of words, and adds ....
Use it when: you want a short preview that does not cut a word in half, like blog post excerpts.
{{ "Organic Assam Masala Chai with Cardamom" | truncatewords: 3 }}Organic Assam Masala...slice
Takes part of a text (or part of a list). First number = where to start (0 is the first letter). Second number = how many. A negative start counts from the end.
Use it when: you need initials, a short code, or the first few items of a list.
{{ "Liquid" | slice: 0 }}
{{ "Liquid" | slice: 2, 4 }}
{{ customer.first_name | slice: 0 }}{{ customer.last_name | slice: 0 }}L
quid
SHsize
Tells you how many characters in a text, or how many items in a list.
Use it when: you check length ("is the title too long?") or count items ("3 products").
{{ "Masala Chai" | size }}
{{ collection.products | size }}
{{ collection.products.size }}11
3
3You can also write .size after a name, like the last line. It works inside if too: {% if product.images.size > 1 %}.
split
Breaks a text into a list, cutting at the separator you choose.
Use it when: you need a list but only have text, for example a comma-separated setting like "S, M, L, XL". It is the only way to make a list in plain Liquid.
{% assign sizes = "S, M, L, XL" | split: ", " %}
{% for size in sizes %}<button>{{ size }}</button>{% endfor %}<button>S</button><button>M</button><button>L</button><button>XL</button>escape
Turns special HTML characters like <, >, & and quotes into safe codes.
Use it when: you print text that a shopper or merchant typed into an HTML attribute (like alt or title), so a quote in the text cannot break your HTML.
{% assign note = 'Tom said "best" chai & cake' %}
<img alt="{{ note | escape }}"><img alt="Tom said "best" chai & cake">Without escape, the " inside the text would end the alt attribute early and break the image tag.
escape_once
Like escape, but it does not escape twice if the text is already escaped.
Use it when: text may already contain codes like &, and you do not want them to turn into &amp;.
{{ "1 < 2 & 3" | escape_once }}
{{ "1 < 2 & 3" | escape_once }}1 < 2 & 3
1 < 2 & 3url_encode
Makes text safe to put in a link. Spaces become +, symbols like @ become codes.
Use it when: you put text into a URL, like a WhatsApp share link or a search link.
https://wa.me/?text={{ "Hi! I want Masala Chai" | url_encode }}https://wa.me/?text=Hi%21+I+want+Masala+Chaiurl_decode
The opposite: turns link codes back into normal text.
Use it when: you read text from a URL and want to show it nicely.
{{ "Masala+Chai%21" | url_decode }}Masala Chai!Filters: numbers
plus
Adds a number.
Use it when: you add up values, like a price plus a gift-wrap charge, or a counter.
{{ 4 | plus: 2 }}
{{ product.price | plus: 5000 | money }}6
₹300.00minus
Subtracts a number.
Use it when: you work out a saving, or how much is left.
{{ product.compare_at_price | minus: product.price | money }}₹50.00times
Multiplies.
Use it when: you multiply price by quantity, or turn a decimal into a percentage.
{{ 3 | times: 2 }}
{{ 0.15 | times: 100 }}6
15.0divided_by
Divides. Careful: if both numbers are whole numbers, the answer is rounded down to a whole number. To keep decimals, divide by a decimal like 3.0.
Use it when: you work out a percentage or an average.
{{ 16 | divided_by: 4 }}
{{ 5 | divided_by: 3 }}
{{ 5 | divided_by: 3.0 }}4
1
1.6666666666666667The classic use, a discount percentage. Multiply by 100 before dividing, or you get 0:
{% assign saving = product.compare_at_price | minus: product.price %}
{{ saving | times: 100 | divided_by: product.compare_at_price }}% off16% offmodulo
Gives the remainder after dividing. 7 divided by 3 is 2, remainder 1.
Use it when: you do something every Nth time, like a banner after every 4th product, or "odd/even" rows.
{% for product in collection.products %}
{{ product.title }}
{% assign r = forloop.index | modulo: 4 %}
{% if r == 0 %}<div class="promo-banner">Free delivery on all teas</div>{% endif %}
{% endfor %}round
Rounds to the nearest whole number, or to a number of decimal places.
Use it when: you show ratings (4.37 → 4.4) or clean up maths results.
{{ 2.7 | round }}
{{ 4.37 | round: 1 }}3
4.4ceil
Rounds up to the next whole number.
Use it when: you need "at least": pages needed, boxes needed, full stars.
{{ 1.2 | ceil }}
{{ 25 | divided_by: 10.0 | ceil }} pages2
3 pagesfloor
Rounds down to the whole number.
Use it when: you show full stars for a rating (4.7 → 4 full stars).
{{ 4.7 | floor }}4abs
Removes the minus sign (gives the absolute value).
Use it when: you show a difference and only care how big it is, not which way.
{{ -17 | abs }}
{{ 4 | abs }}17
4at_least
Makes sure a number is not lower than a minimum.
Use it when: a value must never go below a limit, like a quantity of at least 1.
{{ 0 | at_least: 1 }}
{{ 4 | at_least: 1 }}1
4at_most
Makes sure a number is not higher than a maximum.
Use it when: you cap a value, like "show at most 10 left" or a 5-star rating.
{{ 25 | at_most: 10 }}
{{ 3 | at_most: 10 }}10
3Filters: lists
These filters work on lists (arrays), like collection.products, product.tags or a list you made with split.
first and last
Give the first or last item of a list.
Use it when: you need one item, like the first image or the newest tag. You can also write .first and .last.
{{ product.tags | first }}
{{ product.images.first | image_url: width: 300 | image_tag }}
{{ "S, M, L" | split: ", " | last }}organic
<img src="…/masala-chai.jpg?width=300" …>
Ljoin
Joins a list into one text, with a separator you choose between items.
Use it when: you print a list in a sentence: "Tags: organic, assam, bestseller".
Tags: {{ product.tags | join: ", " }}Tags: organic, assam, bestsellermap
Takes one field from every item and makes a new list of just those values. Like taking only the names from a class register.
Use it when: you need, for example, all the titles or all the vendors from a list of products.
{{ collection.products | map: "title" | join: ", " }}Masala Chai, Green Tea, Clay Cupwhere
Keeps only the items that match. Like a sieve.
Use it when: you want, for example, only in-stock products, or only products of one type.
{% assign teas = collection.products | where: "type", "Tea" %}
{% assign in_stock = collection.products | where: "available" %}
Teas: {{ teas | map: "title" | join: ", " }}
In stock: {{ in_stock.size }}Teas: Masala Chai, Green Tea
In stock: 2With one value (where: "available"), it keeps items where that field is "yes".
sort
Puts a list in order. Capital letters come before small letters ("Zebra" before "apple").
Use it when: you sort by a field, like price: sort: "price".
{% assign cheapest_first = collection.products | sort: "price" %}
{{ cheapest_first | map: "title" | join: ", " }}Clay Cup, Green Tea, Masala Chaisort_natural
Sorts text A to Z, ignoring capital letters. This is the order people expect.
Use it when: you sort names or titles for people to read.
{% assign names = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}
sort: {{ names | sort | join: ", " }}
sort_natural: {{ names | sort_natural | join: ", " }}sort: Sally Snake, giraffe, octopus, zebra
sort_natural: giraffe, octopus, Sally Snake, zebraSee the difference? sort puts every capital letter first. sort_natural reads like a dictionary.
reverse
Flips a list: last becomes first.
Use it when: you want the opposite order, like newest first. To reverse text, split it into letters first.
{{ "1, 2, 3" | split: ", " | reverse | join: ", " }}
{{ "chai" | split: "" | reverse | join: "" }}3, 2, 1
iahcuniq
Removes duplicates, keeping one of each.
Use it when: you collect values from many products (like all vendors) and each should show once.
{{ collection.products | map: "vendor" | uniq | join: ", " }}Chai Corner, Clay Craftscompact
Removes empty (nil) items from a list.
Use it when: you map a field that some items do not have, and you do not want blank spots.
{% assign images = collection.products | map: "featured_image" | compact %}
{{ images.size }} products have a photo.2 products have a photo.concat
Joins two lists into one.
Use it when: you combine two collections or two tag lists into one list to loop over.
{% assign teas = "Masala, Green" | split: ", " %}
{% assign extras = "Cup, Kettle" | split: ", " %}
{{ teas | concat: extras | join: ", " }}Masala, Green, Cup, Kettlesum
Adds up the numbers in a list, or one number field from every item.
Use it when: you total something: the quantity of all cart items, or the weight.
{{ cart.items | sum: "quantity" }} items in your cart4 items in your cartFilters: other
date
Shows a date and time in the format you choose, using codes like %d (day), %B (month name), %Y (year). "now" means the current date and time.
Use it when: you show when a blog post was published, an order date, or the current year in the footer.
{{ article.published_at | date: "%d %B %Y" }}
© {{ "now" | date: "%Y" }} {{ shop.name }}17 July 2026
© 2026 Chai Corner| Code | Means | Example |
|---|---|---|
%d | day of month | 07 |
%B / %b | month name / short | October / Oct |
%Y / %y | year / short year | 2026 / 26 |
%A / %a | weekday / short | Wednesday / Wed |
%H:%M | hours:minutes (24h) | 14:30 |
%I:%M %p | hours:minutes (12h) | 02:30 PM |
default
Shows a backup value when the real value is nil, false or empty.
Use it when: a setting or metafield might be empty, and you never want a blank space.
{{ product.metafields.custom.subtitle | default: "Freshly packed every week" }}
{{ section.settings.heading | default: "Our best sellers" }}Freshly packed every week
Our best sellersdefault treats false as empty. If false is a real answer you want to print, add allow_false: true: {{ setting | default: true, allow_false: true }}.
Quick recap
- Double curly braces print a value. Curly brace with % runs logic. The pipe
|changes a value with a filter. - Types: text, number, true/false, nil, list. Only
falseandnilcount as "no". - Decisions:
if,unless,elsif,else,case/when. - Repeating:
forwithlimit,offset,reversed,break,continue, and theforloophelper. - Files:
rendersnippets and pass in what they need. Do not useinclude. - Saving values:
assignfor one value,capturefor a block of text. - Filters: text (
append,replace,truncate…), numbers (plus,times,divided_by…), lists (map,where,sort,join…), anddateanddefault. - Add
-inside the braces to remove extra blank lines.
Try it yourself
In a Custom Liquid section on your product page, print this one line for the current product:
MASALA CHAI · 3 tags · organic, assam, bestseller · Save 16%
Hints: upcase, size, join, and the discount percentage example from divided_by. Use closest.product instead of product if you put it in a Custom Liquid block inside Product information.
Show solution
{%- assign p = product -%}
{{ p.title | upcase }} · {{ p.tags.size }} tags · {{ p.tags | join: ", " }}
{%- if p.compare_at_price > p.price -%}
{%- assign saving = p.compare_at_price | minus: p.price -%}
{{ ' · Save ' }}{{ saving | times: 100 | divided_by: p.compare_at_price }}%
{%- endif -%}Want to go deeper? The Theme Developer course uses Liquid in every lesson, and live classes go through it with you on your own store.