Shopify Liquid Cheat Sheet 2026

In Guides 0 comments

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.

RELATED ARTICLES