Introduction — The Shopify Liquid Cheat Sheet, Updated for 2026
1. Liquid Basics — Syntax, Handles and Global Variables
The building blocks every Shopify theme developer uses daily: how Liquid is written, how handles work, and the global variables available on every page.
Two types of Liquid markup
Output tags {{ }} print a value; logic tags {% %} run code without printing. Add a hyphen ({{- -}}, {%- -%}) to strip surrounding whitespace.
{{ product.title }}
{% if product.available %}In stock{% endif %}
{%- assign clean = 'no extra whitespace' -%}
What is a handle?
A handle is the URL-friendly, lowercase, hyphenated identifier of a resource, generated from its title. "Summer Sale!" becomes summer-sale. Use handles, not IDs, to reference resources in themes.
{{ collections['summer-sale'].title }}
{{ 'Summer Sale!' | handleize }} → summer-sale
Check if a variable is blank, empty or null
Compare against blank to catch nil, empty strings and empty arrays in one check. Use empty for arrays and strings only.
{% if product.description != blank %}
{{ product.description }}
{% endif %}
page_title
Global variable holding the page's title, used to build the <title> tag in theme.liquid.
<title>{{ page_title }} | {{ shop.name }}</title>
page_description
Global variable holding the resource's SEO description, if one is set. Use it for the meta description tag.
{% if page_description %}
<meta name="description" content="{{ page_description | escape }}">
{% endif %}
layout
Sets which layout file a template uses, or disables the layout entirely. Place it at the top of a Liquid template.
{% layout 'alternate' %}
{% layout none %}
current_tags
On collection and blog pages filtered by tags, returns the tags currently applied.
{% if current_tags %}
Filtered by: {{ current_tags | join: ', ' }}
{% endif %}
Search only products
Add a hidden type field to the search form so results return products only.
<form action="{{ routes.search_url }}">
<input type="hidden" name="type" value="product">
<input type="search" name="q">
</form>
Related: How to build a custom Shopify theme
2. Logic and Control-Flow Tags
Tags control what renders and when. Expand any tag to see what it does and a working example.
{% comment %}
Everything between the tags is ignored and never rendered. Use it for developer notes.
{% comment %}Hide until launch{% endcomment %}
{% raw %}
Outputs Liquid code literally without processing it — useful when a JavaScript template library also uses curly braces.
{% raw %}{{ this is not processed }}{% endraw %}
{% if %}
Renders a block only when a condition is true. Chain with elsif and else.
{% if cart.item_count > 0 %}
Cart has items
{% elsif customer %}
Welcome back
{% else %}
Your cart is empty
{% endif %}
{% unless %}
The opposite of if: renders the block only when the condition is false.
{% unless product.available %}Sold out{% endunless %}
{% case %}
Compares one variable against several values — cleaner than long elsif chains.
{% case product.type %}
{% when 'Shirt' %}Shirt sizing
{% when 'Shoe', 'Boot' %}Shoe sizing
{% else %}Standard sizing
{% endcase %}
{% cycle %}
Inside a loop, outputs the next string from a list each time — typically for alternating classes.
{% for item in cart.items %}
<li class="{% cycle 'odd', 'even' %}">{{ item.title }}</li>
{% endfor %}
{% for %}
Loops over an array or range. Supports limit, offset, reversed, an else block for empty arrays, and break / continue.
{% for product in collection.products limit: 4 %}
{{ product.title }}
{% else %}
No products found.
{% endfor %}
{% tablerow %}
Loops like for but outputs HTML table rows and cells. Set columns with cols.
<table>
{% tablerow product in collection.products cols: 3 %}
{{ product.title }}
{% endtablerow %}
</table>
{% assign %}
Creates or overwrites a variable.
{% assign sale = true %}
{% assign price = product.price | money %}
{% increment %}
Creates a counter starting at 0 and adds 1 each time it is called. It is separate from variables created with assign, even with the same name. Not new — it has been part of Liquid for years.
{% increment count %} → 0
{% increment count %} → 1
{% decrement %}
Creates a counter starting at -1 and subtracts 1 each time it is called. Also independent of assign variables.
{% decrement count %} → -1
{% decrement count %} → -2
{% capture %}
Captures everything rendered between the tags into a string variable.
{% capture greeting %}Hello, {{ customer.first_name }}{% endcapture %}
{{ greeting }}
{% include %} → use {% render %}
Legacy. include shares all parent variables, which makes snippets hard to debug and slows rendering. Use render, which isolates the snippet and passes only the variables you name.
{% comment %} Old {% endcomment %}
{% include 'product-card' %}
{% comment %} Current {% endcomment %}
{% render 'product-card', product: product %}
{% liquid %} and {% echo %}
Writes several tags in one block without repeating delimiters. Use echo to output inside it.
{% liquid
assign title = product.title | upcase
if product.available
echo title
endif
%}
Related: Shopify theme development
3. Liquid Operators
Operators compare values inside if, unless and case conditions.
== and !=
Corrected. Equal to, and not equal to. (Older cheat sheets print this as !-, which is a typo.)
{% if product.type == 'Shirt' %}
{% if product.vendor != 'Acme' %}
> < >= <=
Greater than, less than, greater than or equal to, less than or equal to.
{% if product.price >= 5000 %}Free shipping{% endif %}
or / and
Combine conditions. Liquid has no parentheses, and conditions are evaluated from right to left — nest if tags when order matters.
{% if product.available and product.price < 2000 %}
Deal of the day
{% endif %}
contains
Checks for a substring in a string, or a string in an array of strings. It does not work on arrays of objects.
{% if product.tags contains 'sale' %}On sale{% endif %}
Related: DIY store vs hiring a Shopify expert
4. Loop Helpers (forloop)
Inside any for loop, the forloop object tells you where you are in the loop.
forloop.length
Total number of iterations in the loop.
{{ forloop.length }}
forloop.index
Current iteration, starting at 1.
{{ forloop.index }} of {{ forloop.length }}
forloop.index0
Current iteration, starting at 0.
{% if forloop.index0 == 0 %}First item{% endif %}
forloop.rindex
Iterations remaining, counting down to 1.
{{ forloop.rindex }}
forloop.rindex0
Iterations remaining, counting down to 0.
{{ forloop.rindex0 }}
forloop.first
true on the first iteration.
{% if forloop.first %}<ul>{% endif %}
forloop.last
true on the last iteration.
{{ tag }}{% unless forloop.last %}, {% endunless %}
Related: Shopify development stores in 2026
5. Pagination (paginate)
The paginate object is only available inside a {% paginate %} block, which splits large arrays such as collection products into pages.
{% paginate %} block
Wrap the loop in paginate and set the page size with by.
{% paginate collection.products by 12 %}
{% for product in collection.products %}
{{ product.title }}
{% endfor %}
{{ paginate | default_pagination }}
{% endpaginate %}
paginate.page_size
Number of items per page.
{{ paginate.page_size }}
paginate.current_page
The page number being viewed.
Page {{ paginate.current_page }} of {{ paginate.pages }}
paginate.current_offset
Number of items skipped before the current page.
{{ paginate.current_offset }}
paginate.pages
Total number of pages.
{{ paginate.pages }}
paginate.items
Total number of items across all pages.
{{ paginate.items }} products
paginate.previous / previous.title / previous.url
The previous page as a part object; nil on the first page.
{% if paginate.previous %}
<a href="{{ paginate.previous.url }}">{{ paginate.previous.title }}</a>
{% endif %}
paginate.next / next.url
The next page as a part object; nil on the last page.
{% if paginate.next %}<a href="{{ paginate.next.url }}">Next</a>{% endif %}
paginate.parts and part.is_link
An array of page links for building custom numbered pagination. part.is_link is false for the current page and ellipses.
{% for part in paginate.parts %}
{% if part.is_link %}
<a href="{{ part.url }}">{{ part.title }}</a>
{% else %}
<span>{{ part.title }}</span>
{% endif %}
{% endfor %}
Related: Shopify support and maintenance
6. Standard Liquid Filters (Text, Arrays and Maths)
Filters change output. They follow a pipe, take arguments after a colon, and can be chained left to right.
escape
Escapes HTML characters so text is displayed safely rather than parsed as markup.
{{ '<p>Hi</p>' | escape }}
append
Adds a string to the end.
{{ 'sale' | append: '.jpg' }} → sale.jpg
prepend
Adds a string to the start.
{{ 'world' | prepend: 'Hello ' }}
size
Number of characters in a string or items in an array.
{{ product.tags | size }}
{% if cart.items.size > 3 %}
join
Joins an array into a string with a separator.
{{ product.tags | join: ', ' }}
downcase / upcase
Converts a string to lower or upper case.
{{ product.title | upcase }}
strip_html
Removes all HTML tags.
{{ product.description | strip_html }}
strip_newlines
Removes line breaks.
{{ article.excerpt | strip_newlines }}
truncate
Shortens a string to a character count. The count includes the ellipsis.
{{ product.description | strip_html | truncate: 120 }}
truncatewords
Shortens a string to a number of words.
{{ article.content | strip_html | truncatewords: 30 }}
date
Formats a date with strftime codes, or with a named format from your theme's locale file.
{{ article.published_at | date: '%B %d, %Y' }}
{{ article.published_at | date: format: 'abbreviated_date' }}
first / last
Returns the first or last item of an array.
{{ product.images | first }}
{{ collection.products | last }}
newline_to_br
Converts line breaks into <br> tags.
{{ cart.note | newline_to_br }}
replace / replace_first
Replaces every, or only the first, occurrence of a substring.
{{ product.title | replace: '-', ' ' }}
remove / remove_first
Removes every, or only the first, occurrence of a substring.
{{ 'Hello, world, hello' | remove: 'hello' }}
split
Splits a string into an array on a separator.
{% assign sizes = 'S,M,L' | split: ',' %}
plus / minus
Adds or subtracts a number. Related filters: times, divided_by, modulo, round.
{{ forloop.index | plus: 1 }}
{{ product.price | minus: 500 | money }}
Related: Best Shopify themes
7. Shopify Filters (URLs, HTML, Money and More)
Shopify adds its own filters on top of standard Liquid. Items marked with an asterisk in older cheat sheets only work on tag-filtered collection or blog pages.
asset_url
Returns the CDN URL of a file in the theme's assets folder.
{{ 'theme.css' | asset_url | stylesheet_tag }}
camelize
Converts a string to CamelCase.
{{ 'variant-title' | camelize }} → VariantTitle
capitalize
Capitalizes the first character of a string.
{{ 'summer sale' | capitalize }}
default_pagination
Outputs ready-made pagination links. Use inside a paginate block.
{{ paginate | default_pagination: next: 'Next', previous: 'Prev' }}
global_asset_url
Legacy. Returns the URL of a Shopify-hosted global asset. Rarely needed in modern themes.
{{ 'lightbox.js' | global_asset_url | script_tag }}
handleize (handle)
Converts a string into a handle.
{{ 'Summer Sale!' | handleize }} → summer-sale
img_tag → use image_tag
Legacy. Use image_url with image_tag, which outputs responsive srcset, width and height attributes automatically.
{% comment %} Current {% endcomment %}
{{ product.featured_image | image_url: width: 800 | image_tag: loading: 'lazy' }}
link_to
Wraps text in a link to a URL.
{{ 'Contact us' | link_to: '/pages/contact' }}
link_to_vendor
Links a vendor name to its auto-generated vendor collection.
{{ product.vendor | link_to_vendor }}
link_to_type
Links a product type to its auto-generated type collection.
{{ product.type | link_to_type }}
link_to_tag*
On collection and blog pages, links to the current resource filtered by a tag.
{% for tag in collection.all_tags %}
{{ tag | link_to_tag: tag }}
{% endfor %}
link_to_add_tag*
Links to the current page with a tag added to the active filters.
{{ tag | link_to_add_tag: tag }}
link_to_remove_tag*
Links to the current page with a tag removed from the active filters.
{{ tag | link_to_remove_tag: tag }}
highlight_active_tag*
Wraps a tag in <span class="active"> when it is currently applied.
{{ tag | highlight_active_tag | link_to_tag: tag }}
money
Formats a price using the store's money format. Prices in Liquid are in cents (subunits), so always apply a money filter.
{{ product.price | money }} → $19.99
money_with_currency
Formats a price with the currency code.
{{ product.price | money_with_currency }} → $19.99 USD
money_without_currency
Formats a price as a number without a currency symbol.
{{ product.price | money_without_currency }} → 19.99
pluralize
Picks the singular or plural word based on a number.
{{ cart.item_count }} {{ cart.item_count | pluralize: 'item', 'items' }}
product_img_url → use image_url
Legacy. Deprecated, along with img_url. Use image_url with a width or height parameter.
{{ product.featured_image | image_url: width: 600 }}
sort_by
Adds a sort parameter to a collection URL.
{{ collection.url | sort_by: 'price-ascending' }}
script_tag
Outputs a <script> tag. It is render-blocking, so for performance prefer a manual <script src="…" defer>.
<script src="{{ 'app.js' | asset_url }}" defer></script>
stylesheet_tag
Outputs a <link rel="stylesheet"> tag for a URL.
{{ 'theme.css' | asset_url | stylesheet_tag }}
url_for_type
Returns the URL of a product type's collection page.
{{ 'Shirts' | url_for_type }}
url_for_vendor
Returns the URL of a vendor's collection page.
{{ 'Acme' | url_for_vendor }}
weight_with_unit
Formats a weight (stored in grams) using the store's weight unit.
{{ variant.weight | weight_with_unit }}
within
Scopes a product URL to a collection, producing /collections/…/products/…. These URLs duplicate the canonical product URL, so many SEO-focused themes avoid them.
{{ product.url | within: collection }}
Related: Shopify app development
8. Shopify Image Sizes: Legacy Names vs image_url
The named sizes below belonged to the old img_url filter. They still appear in older themes, but new code should request exact pixel widths with image_url.
pico — 16×16
Legacy. Legacy size name. Modern equivalent: image_url: width: 16.
{{ image | image_url: width: 16 }}
icon — 32×32
Legacy. Legacy size name. Modern equivalent: image_url: width: 32.
{{ image | image_url: width: 32 }}
thumb — 50×50
Legacy. Legacy size name. Modern equivalent: image_url: width: 50.
{{ image | image_url: width: 50 }}
small — 100×100
Legacy. Legacy size name. Modern equivalent: image_url: width: 100.
{{ image | image_url: width: 100 }}
compact — 160×160
Legacy. Legacy size name. Modern equivalent: image_url: width: 160.
{{ image | image_url: width: 160 }}
medium — 240×240
Legacy. Legacy size name. Modern equivalent: image_url: width: 240.
{{ image | image_url: width: 240 }}
large — 480×480
Legacy. Legacy size name. Modern equivalent: image_url: width: 480.
{{ image | image_url: width: 480 }}
grande — 600×600
Legacy. Legacy size name. Modern equivalent: image_url: width: 600.
{{ image | image_url: width: 600 }}
1024x1024 and 2048x2048
Legacy. Legacy size names. Modern equivalent: image_url: width: 1024 or width: 2048.
{{ image | image_url: width: 2048 }}
master
Corrected. Returned the original uploaded image at full size — not a fixed 2048px, as some cheat sheets state. Avoid serving originals on storefronts; request the width you need.
{{ image | image_url: width: 1600 }}
Responsive images, the modern way
Let image_tag generate srcset and sizes so each device downloads the right file — better for Core Web Vitals.
{{ product.featured_image
| image_url: width: 1200
| image_tag: widths: '400, 800, 1200', sizes: '(min-width: 750px) 50vw, 100vw' }}
Related: Shopify theme and speed audits
9. Theme Settings (Input Types)
Theme and section settings are defined as JSON in config/settings_schema.json and in each section's {% schema %}. Merchants edit them in the theme editor; you read them with settings.id or section.settings.id.
Text field
Single-line text input.
{ "type": "text", "id": "heading", "label": "Heading" }
{{ section.settings.heading }}
Multiline text field
Use the textarea type, or richtext when merchants need formatting.
{ "type": "textarea", "id": "intro", "label": "Intro" }
Select menus
A dropdown of fixed options. Use radio for a small number of visible choices.
{ "type": "select", "id": "layout", "label": "Layout",
"options": [
{ "value": "grid", "label": "Grid" },
{ "value": "list", "label": "List" }
], "default": "grid" }
Checkboxes
A true/false toggle.
{ "type": "checkbox", "id": "show_vendor", "label": "Show vendor", "default": true }
{% if section.settings.show_vendor %}{{ product.vendor }}{% endif %}
File uploads → image_picker and video
Legacy. The old settings.html file-upload inputs were replaced by resource settings such as image_picker, video and video_url.
{ "type": "image_picker", "id": "banner", "label": "Banner image" }
{{ section.settings.banner | image_url: width: 1600 | image_tag }}
CSS / specialty settings → color, font_picker, range
Legacy. The old CSS-class-based inputs were replaced by typed settings: color, color_scheme, font_picker, range and more.
{ "type": "range", "id": "columns", "label": "Columns",
"min": 2, "max": 6, "step": 1, "default": 4 }
Shopify credit
Outputs the "Powered by Shopify" link, usually in the footer.
{{ powered_by_link }}
Related: Shopify web design
10. Blog, Article and Comment Objects
Available on blog and article templates, and anywhere through the global blogs object.
blogs['the-handle']
Access any blog by handle from any template.
{% for article in blogs['news'].articles limit: 3 %}
{{ article.title }}
{% endfor %}
blog.id / blog.handle / blog.title / blog.url
Core identifiers and the blog's URL.
<a href="{{ blog.url }}">{{ blog.title }}</a>
blog.articles / blog.articles_count
The blog's articles, and how many there are. Paginate large blogs.
{{ blog.articles_count }} articles
blog.comments_enabled? / blog.moderated?
Whether comments are allowed, and whether they need approval before appearing.
{% if blog.comments_enabled? %}…{% endif %}
blog.next_article / blog.previous_article
On an article page, the adjacent articles — use for next/previous navigation.
{% if blog.next_article %}
<a href="{{ blog.next_article.url }}">{{ blog.next_article.title }}</a>
{% endif %}
blog.all_tags / blog.tags
all_tags lists every tag used in the blog; tags lists tags on the articles currently shown.
{% for tag in blog.all_tags %}{{ tag | link_to_tag: tag }}{% endfor %}
article.id / article.title / article.author / article.url
Core article details.
<h1>{{ article.title }}</h1>
<p>By {{ article.author }}</p>
article.content / article.excerpt
The full body, and the summary entered in the Excerpt field. Use article.excerpt_or_content as a fallback.
{{ article.excerpt_or_content }}
article.created_at / article.published_at
Timestamps — format with the date filter.
{{ article.published_at | date: '%d %b %Y' }}
article.image
The article's featured image.
{% if article.image %}
{{ article.image | image_url: width: 1200 | image_tag }}
{% endif %}
article.tags
The article's tags.
{{ article.tags | join: ', ' }}
article.comments / comments_count / comment_post_url
Published comments, their count, and the URL comment forms post to.
{{ article.comments_count }} comments
article.comments_enabled? / article.moderated?
Comment settings inherited from the blog.
{% if article.comments_enabled? %}{% form 'new_comment', article %}…{% endform %}{% endif %}
comment.id / author / email / content / status / url
Details of a single comment. status reflects moderation.
{% for comment in article.comments %}
<p><strong>{{ comment.author }}</strong>: {{ comment.content }}</p>
{% endfor %}
Related: FoldTech articles
11. Collection Object
Available on collection templates, and anywhere through collections['handle'].
collections['the-handle']
Access any collection by handle.
{% for product in collections['best-sellers'].products limit: 8 %}…{% endfor %}
collection.id / title / handle / url
Core identifiers.
<a href="{{ collection.url }}">{{ collection.title }}</a>
collection.description
The collection's description (HTML).
{{ collection.description }}
collection.image / collection.featured_image
The collection's image, or the first product image if none is set.
{{ collection.featured_image | image_url: width: 800 | image_tag }}
collection.products
Products in the collection — paginate for more than 50.
{% paginate collection.products by 24 %}…{% endpaginate %}
collection.products_count / all_products_count
Products in the current (filtered) view, and in the whole collection.
{{ collection.products_count }} of {{ collection.all_products_count }}
collection.all_products
Corrected. Not listed in Shopify's current collection object reference. Use collection.products with pagination instead.
{% paginate collection.products by 50 %}…{% endpaginate %}
collection.all_types / all_vendors
Every product type and vendor in the collection.
{% for vendor in collection.all_vendors %}{{ vendor | link_to_vendor }}{% endfor %}
collection.tags / all_tags
Tags on the products currently shown, and across the whole collection.
{{ collection.all_tags | join: ', ' }}
collection.filters
Storefront filters (price, availability, product options) for building filter UIs — the modern replacement for tag filtering.
{% for filter in collection.filters %}{{ filter.label }}{% endfor %}
collection.next_product / previous_product
On a product page reached through a collection, the adjacent products in that collection.
{% if collection.next_product %}
<a href="{{ collection.next_product.url }}">Next</a>
{% endif %}
Related: Shopify CRO
12. Page Object
Available on page templates, and anywhere through the global pages object.
pages['the-handle']
Corrected. Access any page by handle. Older cheat sheets write this as page['the-handle'], which doesn't work.
{{ pages['shipping-policy'].content }}
page.id / page.handle / page.title / page.url
Core identifiers.
<a href="{{ page.url }}">{{ page.title }}</a>
page.content
The page body (HTML).
{{ page.content }}
page.author
The author of the page.
{{ page.author }}
page.metafields
Custom data attached to the page.
{{ page.metafields.custom.subtitle }}
Related: More FoldTech guides
13. Product and Variant Objects
Available on product templates, in collection loops, and through all_products['handle'].
all_products['the-handle']
Access a product by handle from any template. Limited to 20 unique handles per page — use a collection or product list setting for more.
{{ all_products['classic-tee'].title }}
product.id / title / handle / url
Core identifiers.
<a href="{{ product.url }}">{{ product.title }}</a>
product.type / product.vendor
The product type and vendor.
{{ product.vendor | link_to_vendor }}
product.price / price_min / price_max
Prices in cents (subunits). price equals price_min.
From {{ product.price_min | money }}
product.compare_at_price_min / compare_at_price_max
The lowest and highest compare-at prices across variants.
{% if product.compare_at_price_min > product.price_min %}On sale{% endif %}
product.price_varies / compare_at_price_varies
true when variants have different prices or compare-at prices.
{% if product.price_varies %}From {% endif %}{{ product.price | money }}
product.featured_image / images / media
The main image, all images, and all media including video and 3D. Prefer media in modern galleries.
{% for media in product.media %}{{ media | image_url: width: 800 | image_tag }}{% endfor %}
product.description / product.content
The product description (HTML). content is an alias.
{{ product.description }}
product.variants
All variants of the product.
{% for variant in product.variants %}{{ variant.title }}{% endfor %}
product.available
true if at least one variant can be purchased.
{% unless product.available %}Sold out{% endunless %}
product.template_suffix
The alternate template assigned to the product, if any.
{{ product.template_suffix }}
product.selected_variant
The variant selected with the ?variant= URL parameter, otherwise nil.
{{ product.selected_variant.title }}
product.selected_or_first_available_variant
The selected variant, or the first available one. The standard default for product forms.
{% assign current = product.selected_or_first_available_variant %}
{{ current.price | money }}
product.collections / product.tags
The collections the product belongs to, and its tags.
{{ product.tags | join: ', ' }}
product.options / options_with_values
Option names, and option names with their values — use the latter to build variant pickers.
{% for option in product.options_with_values %}
{{ option.name }}: {{ option.values | join: ', ' }}
{% endfor %}
variant.id / title / sku
Core variant identifiers. Post variant.id to add to cart.
<input type="hidden" name="id" value="{{ variant.id }}">
variant.price / compare_at_price
The variant's price and compare-at price, in cents.
{{ variant.price | money }} <s>{{ variant.compare_at_price | money }}</s>
variant.image
The image assigned to the variant.
{{ variant.image | image_url: width: 400 | image_tag }}
variant.available
true if the variant can be purchased.
{% if variant.available %}Add to cart{% else %}Sold out{% endif %}
variant.inventory_management / inventory_quantity
Whether Shopify tracks the variant's stock, and the quantity available.
{% if variant.inventory_quantity < 5 %}Only a few left{% endif %}
variant.weight
The variant's weight in grams — format with weight_with_unit.
{{ variant.weight | weight_with_unit }}
variant.option1 / option2 / option3
The variant's value for each product option.
{{ variant.option1 }} / {{ variant.option2 }}
Related: Shopify Plus development
14. Image Object
Returned by product.images, product.featured_image, variant.image and similar.
image.id / image.product_id / image.position
The image ID, its product's ID, and its position in the product's image list.
{{ image.position }}
image.src
The image's relative path. Pass the image to image_url instead of building URLs from src.
{{ image | image_url: width: 800 }}
image.alt
The alt text — always output it for accessibility and SEO.
{{ image | image_url: width: 800 | image_tag: alt: image.alt }}
image.width / height / aspect_ratio
Dimensions — use them to reserve space and prevent layout shift.
style="aspect-ratio: {{ image.aspect_ratio }}"
Related: Ecommerce SEO
15. Customer Object
Only available when a customer is logged in — always check {% if customer %} first.
customer.first_name / last_name / email / id
Core customer details.
{% if customer %}Welcome back, {{ customer.first_name }}{% endif %}
customer.accepts_marketing
Whether the customer has agreed to receive marketing email.
{% if customer.accepts_marketing %}Subscribed{% endif %}
customer.has_account
true if the customer has created an account.
{{ customer.has_account }}
customer.addresses / addresses_count / default_address
Saved addresses, how many there are, and the default one.
{{ customer.default_address.city }}
customer.orders / orders_count
Past orders — used on account templates.
{{ customer.orders_count }} orders
Related: Klaviyo and email marketing
16. Cart Object
Available on every page through the global cart object.
cart.item_count
Total quantity of items in the cart.
{{ cart.item_count }} {{ cart.item_count | pluralize: 'item', 'items' }}
cart.items
The cart's line items.
{% for item in cart.items %}
{{ item.title }} × {{ item.quantity }} — {{ item.final_line_price | money }}
{% endfor %}
cart.total_price
Total price after discounts, in cents.
{{ cart.total_price | money }}
cart.total_weight
Total weight in grams.
{{ cart.total_weight | weight_with_unit }}
cart.note
The order note entered by the customer.
<textarea name="note">{{ cart.note }}</textarea>
cart.attributes
Custom information collected in the cart, submitted with attributes[Name] fields.
<input name="attributes[Gift message]" value="{{ cart.attributes['Gift message'] }}">
Related: Shopify checkout mistakes to avoid
17. Linklist (Menu) and Link Objects
Navigation menus built in Online Store → Navigation. Access them with linklists['handle'].
linklists['the-handle']
Access any menu by handle.
{% for link in linklists['main-menu'].links %}
<a href="{{ link.url }}">{{ link.title }}</a>
{% endfor %}
linklist.title / linklist.handle / linklist.links
The menu's title, handle and links.
{{ linklists['footer'].title }}
linklist.object
Corrected. Not an attribute of the menu itself — object belongs to each link.
{{ link.object.title }}
link.title / link.url
The link's text and destination.
<a href="{{ link.url }}">{{ link.title }}</a>
link.active / link.current
active is true when the link matches the current page; current when it matches exactly. Use for highlighting navigation.
<a class="{% if link.active %}is-active{% endif %}" href="{{ link.url }}">
link.type / link.object
What the link points to (for example collection_link or http_link) and the linked resource itself.
{% if link.type == 'collection_link' %}{{ link.object.products_count }}{% endif %}
link.links (nested menus)
Child links for dropdown and mega menus.
{% for child in link.links %}{{ child.title }}{% endfor %}
Related: How to add navigation in Shopify
18. Shop Object
Store-wide information available on every page.
shop.name / description / email
The store's name, description and customer-facing email.
© {{ 'now' | date: '%Y' }} {{ shop.name }}
shop.url / domain / permanent_domain
The primary URL, primary domain, and the permanent .myshopify.com domain.
{{ shop.permanent_domain }}
shop.address.summary / street / city / province / country / zip
The store's address, in full or by part.
{{ shop.address.summary }}
shop.currency / money_format / money_with_currency_format
Corrected. The store currency and its formats. (Older sheets misspell the last one as money_with_currecny_format.)
{{ shop.currency }}
shop.enabled_payment_types
Enabled payment methods — commonly used to show payment icons.
{% for type in shop.enabled_payment_types %}
{{ type | payment_type_svg_tag }}
{% endfor %}
shop.metafields
Custom data stored at shop level.
{{ shop.metafields.custom.announcement }}
shop.locale
Corrected. The store's primary locale. For the visitor's active language, use request.locale.
{{ request.locale.iso_code }}
shop.password_message
The message shown on the password page.
{{ shop.password_message }}
shop.products_count / collections_count
Number of products and collections in the store.
{{ shop.products_count }} products
shop.vendors / shop.types
Every vendor and product type in the store.
{% for vendor in shop.vendors %}{{ vendor | link_to_vendor }}{% endfor %}
Related: Shopify internationalisation
FAQ — Shopify Liquid
What is Shopify Liquid?
Liquid is the open-source template language Shopify created to build Online Store themes. It combines output tags ({{ }}), logic tags ({% %}) and filters to turn store data — products, collections, carts — into HTML.
Is Shopify Liquid still used in 2026?
Yes. Every Online Store theme, including Shopify's own themes, is built with Liquid. Headless storefronts built with Hydrogen are the exception: they use React and the Storefront API rather than Liquid templates.
What is the difference between {{ }} and {% %} in Liquid?
{{ }} outputs a value to the page. {% %} runs logic — conditions, loops, variable assignment — without printing anything itself.
Should I use include or render in Shopify?
Use render. include is deprecated because it gives snippets access to every parent variable, which makes code harder to debug and slower to render. render isolates the snippet and receives only the variables you pass.
Why was img_url replaced by image_url?
image_url requests an exact width or height instead of a fixed named size, and it pairs with image_tag to generate responsive srcset markup automatically — smaller downloads on mobile and better Core Web Vitals.
How do I check if a Liquid variable is empty?
Compare it with blank: {% if variable != blank %}. It catches nil values, empty strings and empty arrays in one check.