Shopify Liquid for Beginners — Complete Introduction
Liquid is the template language that powers every Shopify storefront. If you want to customize a Shopify theme, build a new one from scratch, or simply understand how your store's pages are generated, learning Liquid is the essential first step. This guide walks you through everything you need to get started — no prior experience required.
What is Shopify Liquid?
Liquid is a template language originally created by Shopify co-founder Tobias Lütke. It was built to give theme developers a safe, easy-to-read way to generate dynamic HTML for online stores.
Here are the key things to know about Liquid:
- Created by Shopify — Liquid was purpose-built for e-commerce themes. It understands products, collections, carts, and customers out of the box.
- Server-side rendering — Liquid runs on Shopify's servers. When a customer visits your store, Shopify processes the Liquid code and sends back plain HTML to the browser.
- Ruby-based and open source — Liquid is written in Ruby and available on GitHub. While you don't need to know Ruby, understanding that Liquid sits between your HTML and Shopify's data helps you reason about how it works.
- Safe by design — Liquid intentionally limits what code can do. There's no file system access or arbitrary code execution. This keeps Shopify stores secure even when third-party themes are installed.
Think of Liquid as the glue between your store's data (products, prices, images) and the HTML that customers see in their browser.
<!-- Liquid turns this... -->
<h1>{{ product.title }}</h1>
<p>{{ product.price | money }}</p>
<!-- ...into this HTML for the browser -->
<h1>Classic Leather Jacket</h1>
<p>$129.00</p>Where is Liquid Used?
Liquid shows up in several places across a Shopify store. Understanding the file structure helps you know where to look when making changes.
Theme Templates (.liquid files)
Every Shopify theme is made up of .liquid files organized into a specific folder structure. Here's how a typical theme is organized:
- layout/ — Contains
theme.liquid, the master wrapper for every page. Think of it like an HTML shell that wraps all your content. - templates/ — Page-level templates like
product.liquid,collection.liquid, andcart.liquid. Each one controls a specific type of page. - sections/ — Modular, reusable blocks that merchants can customize through the theme editor. For example, a featured product section or a slideshow.
- snippets/ — Small, reusable pieces of code you can include in templates and sections. Great for things like product cards or icon SVGs.
- assets/ — CSS, JavaScript, and image files. These aren't Liquid files themselves but can be referenced from Liquid templates.
- config/ — JSON files that store theme settings and the data behind the theme editor.
- locales/ — Translation files for multi-language stores.
<!-- layout/theme.liquid — the outer shell of every page -->
<html>
<head>
<title>{{ page_title }}</title>
{{ content_for_header }}
</head>
<body>
{{ content_for_layout }}
</body>
</html>Sections and Snippets
Sections are the building blocks of modern Shopify themes (Online Store 2.0). They allow merchants to add, remove, and rearrange content using the theme editor — without touching code.
<!-- sections/featured-product.liquid -->
<div class="featured-product">
<h2>{{ section.settings.heading }}</h2>
<p>{{ section.settings.description }}</p>
</div>
<!-- Including a snippet from another file -->
{% render 'product-card', product: featured_product %}Email Notifications
Shopify also uses Liquid for transactional emails — order confirmations, shipping updates, and more. You can customize these templates in Settings > Notifications in your Shopify admin.
<!-- Order confirmation email template -->
<p>Hi {{ customer.first_name }},</p>
<p>Thanks for your order! Here's what you purchased:</p>
{% for line_item in line_items %}
<p>{{ line_item.title }} x {{ line_item.quantity }} — {{ line_item.price | money }}</p>
{% endfor %}The Three Building Blocks of Liquid
Every piece of Liquid code you write uses one or more of three fundamental building blocks: objects, tags, and filters. Master these three concepts and you can build anything.
Objects — Outputting Data with {{ }}
Objects are how you display data on the page. They use double curly braces. When Shopify sees an object tag, it replaces it with the actual value from your store.
<!-- Objects output data using double curly braces -->
{{ shop.name }} <!-- outputs: "My Awesome Store" -->
{{ product.title }} <!-- outputs: "Classic T-Shirt" -->
{{ product.price | money }} <!-- outputs: "$25.00" -->
{{ cart.item_count }} <!-- outputs: "3" -->
<!-- Use them anywhere inside your HTML -->
<h1>{{ product.title }}</h1>
<img src="{{ product.featured_image | image_url: width: 400 }}" alt="{{ product.title }}">Tags — Logic and Control Flow with {% %}
Tags let you add logic to your templates. They use curly braces with percent signs. Tags don't output anything themselves — they control what gets rendered and how.
<!-- Conditional: show content based on a condition -->
{% if product.available %}
<button>Add to Cart</button>
{% else %}
<p>Sold Out</p>
{% endif %}
<!-- Loop: repeat content for each item -->
{% for product in collection.products %}
<p>{{ product.title }}</p>
{% endfor %}
<!-- Variable assignment -->
{% assign greeting = 'Welcome to our store!' %}
<p>{{ greeting }}</p>Filters — Modifying Output with |
Filters transform the output of an object. They're applied using a pipe character (|) and can be chained together. Think of them as small functions that modify data before it's displayed.
<!-- String filters -->
{{ "hello world" | upcase }} <!-- "HELLO WORLD" -->
{{ product.title | downcase }} <!-- "classic t-shirt" -->
{{ "hello world" | capitalize }} <!-- "Hello world" -->
<!-- Money filter — formats cents as currency -->
{{ product.price | money }} <!-- "$25.00" -->
{{ product.price | money_with_currency }} <!-- "$25.00 USD" -->
<!-- Chaining filters — left to right -->
{{ product.title | downcase | replace: ' ', '-' }}
<!-- "classic-t-shirt" -->
<!-- Date filter -->
{{ article.published_at | date: '%B %d, %Y' }}
<!-- "January 15, 2026" -->Your First Liquid Code
Let's put the building blocks together with real-world examples you'd actually use in a Shopify theme. We'll start simple and build up.
A Simple Product Display
Here's a basic product page template that displays a product's image, title, price, and description. This is the kind of code you'd find in a templates/product.liquid file.
<div class="product-page">
<img
src="{{ product.featured_image | image_url: width: 600 }}"
alt="{{ product.featured_image.alt }}"
>
<h1>{{ product.title }}</h1>
<p class="price">{{ product.price | money }}</p>
<div class="description">
{{ product.description }}
</div>
</div>Using Variables with Assign
The assign tag creates a variable that you can reuse throughout your template. This keeps your code clean and makes it easy to change values in one place.
<!-- Create variables with assign -->
{% assign sale_price = product.price | minus: 500 %}
{% assign product_url = product.url %}
{% assign on_sale = true %}
<!-- Use them later in your template -->
{% if on_sale %}
<p class="original-price">Was: {{ product.price | money }}</p>
<p class="sale-price">Now: {{ sale_price | money }}</p>
{% endif %}
<a href="{{ product_url }}">View Product</a>Basic Conditional — Check if a Product is Available
Conditionals let you show or hide content based on data. The most common pattern is checking whether a product is in stock before showing the Add to Cart button.
{% if product.available %}
<button type="submit" class="btn">
Add to Cart — {{ product.price | money }}
</button>
{% else %}
<button class="btn btn-disabled" disabled>
Sold Out
</button>
{% endif %}
<!-- You can also check compare_at_price for sale items -->
{% if product.compare_at_price > product.price %}
<span class="badge-sale">Sale</span>
{% endif %}Simple Loop — Iterate Over Collection Products
Loops let you repeat a block of HTML for each item in a list. The for loop is the most common — you'll use it constantly for product grids, navigation menus, and more.
<div class="product-grid">
{% for product in collection.products %}
<div class="product-card">
<a href="{{ product.url }}">
<img src="{{ product.featured_image | image_url: width: 300 }}" alt="{{ product.title }}">
<h3>{{ product.title }}</h3>
<p>{{ product.price | money }}</p>
</a>
</div>
{% endfor %}
</div>
<!-- Limit to first 4 products -->
{% for product in collection.products limit: 4 %}
<p>{{ product.title }}</p>
{% endfor %}Working with Shopify Objects
Shopify provides a rich set of objects that map directly to your store's data. Every product you add, every collection you create, and every order that comes in — they all become objects you can access in Liquid. Here are the most important ones to know.
The Product Object
The product object represents a single product in your store. It holds everything you've entered in the Shopify admin — the title, description, price, images, variants, and more.
<!-- Basic product properties -->
{{ product.title }} <!-- "Running Shoes" -->
{{ product.description }} <!-- Full HTML description -->
{{ product.price | money }} <!-- "$89.00" -->
{{ product.vendor }} <!-- "Nike" -->
{{ product.type }} <!-- "Footwear" -->
{{ product.url }} <!-- "/products/running-shoes" -->
{{ product.available }} <!-- true or false -->
<!-- Product images -->
{{ product.featured_image | image_url: width: 500 }}
{{ product.images.size }} <!-- number of images -->
<!-- Product variants (sizes, colors, etc.) -->
{% for variant in product.variants %}
{{ variant.title }} — {{ variant.price | money }}
{% endfor %}The Collection Object
Collections group products together. In the Shopify admin, you create collections like "Summer Sale" or "Men's Shoes". In Liquid, you access these through the collection object.
<!-- Collection properties -->
{{ collection.title }} <!-- "Summer Sale" -->
{{ collection.description }} <!-- Collection description -->
{{ collection.products_count }} <!-- 24 -->
{{ collection.url }} <!-- "/collections/summer-sale" -->
<!-- Display all products in a collection -->
<h1>{{ collection.title }}</h1>
<p>{{ collection.products_count }} products</p>
{% for product in collection.products %}
<div>
<h3>{{ product.title }}</h3>
<p>{{ product.price | money }}</p>
</div>
{% endfor %}The Cart Object
The cart object contains everything in the customer's shopping cart. You'll use it to build the cart page and display cart info in the header.
<!-- Cart summary in the header -->
<a href="/cart">
Cart ({{ cart.item_count }}) — {{ cart.total_price | money }}
</a>
<!-- Full cart page -->
{% for item in cart.items %}
<div class="cart-item">
<img src="{{ item.image | image_url: width: 100 }}" alt="{{ item.title }}">
<p>{{ item.product.title }}</p>
<p>Qty: {{ item.quantity }}</p>
<p>{{ item.line_price | money }}</p>
</div>
{% endfor %}
<p>Subtotal: {{ cart.total_price | money }}</p>How Objects Relate to the Shopify Admin
Every Liquid object maps to something in your Shopify admin dashboard. When you type a product title in the admin, that becomes product.title in Liquid. When you set a price, it becomes product.price. This direct mapping is what makes Liquid powerful — your data flows automatically from the admin to the storefront without any extra work.
<!-- Where Liquid data comes from: -->
<!-- Shopify Admin → Products → "Classic T-Shirt" -->
{{ product.title }} <!-- → "Classic T-Shirt" -->
<!-- Shopify Admin → Products → Price: $25.00 -->
{{ product.price | money }} <!-- → "$25.00" -->
<!-- Shopify Admin → Online Store → Navigation -->
{% for link in linklists.main-menu.links %}
<a href="{{ link.url }}">{{ link.title }}</a>
{% endfor %}Common Liquid Patterns
These are patterns you'll use again and again when building Shopify themes. Save these as reference — they cover the most frequent tasks.
Showing and Hiding Elements Based on Conditions
Use if, elsif, and unless to control what visitors see. You can check product availability, customer login status, cart contents, and more.
<!-- Show a sale badge when there's a compare-at price -->
{% if product.compare_at_price > product.price %}
{% assign savings = product.compare_at_price | minus: product.price %}
<span class="badge">Save {{ savings | money }}</span>
{% endif %}
<!-- Show different content for logged-in customers -->
{% if customer %}
<p>Welcome back, {{ customer.first_name }}!</p>
{% else %}
<a href="/account/login">Log in</a>
{% endif %}
<!-- "unless" is the opposite of "if" -->
{% unless cart.item_count == 0 %}
<a href="/cart">View Cart ({{ cart.item_count }})</a>
{% endunless %}Looping Through Products with Extra Details
The for loop gives you access to a special forloop object with useful properties like forloop.index, forloop.first, and forloop.last.
<div class="product-grid">
{% for product in collection.products %}
<div class="product-card {% if forloop.first %}featured{% endif %}">
<img src="{{ product.featured_image | image_url: width: 400 }}" alt="{{ product.title }}">
<h3>{{ product.title }}</h3>
<p>{{ product.price | money }}</p>
{% if product.compare_at_price > product.price %}
<s>{{ product.compare_at_price | money }}</s>
{% endif %}
</div>
<!-- Add a divider between items, but not after the last one -->
{% unless forloop.last %}
<hr>
{% endunless %}
{% endfor %}
</div>
<!-- Handle empty collections -->
{% for product in collection.products %}
<p>{{ product.title }}</p>
{% else %}
<p>No products found in this collection.</p>
{% endfor %}Formatting Prices
Shopify stores prices in cents (so $25.00 is stored as 2500). The money family of filters converts cents into human-readable currency formats.
<!-- Basic money formatting -->
{{ product.price | money }} <!-- "$25.00" -->
{{ product.price | money_with_currency }} <!-- "$25.00 USD" -->
{{ product.price | money_without_currency }} <!-- "25.00" -->
<!-- Show original and sale price together -->
{% if product.compare_at_price > product.price %}
<span class="original-price">
<s>{{ product.compare_at_price | money }}</s>
</span>
<span class="sale-price">
{{ product.price | money }}
</span>
{% else %}
<span>{{ product.price | money }}</span>
{% endif %}Using Default Values with the Default Filter
The default filter provides a fallback value when a variable is empty, nil, or false. This prevents blank spots in your theme when data is missing.
<!-- Fallback for missing product image alt text -->
<img
src="{{ product.featured_image | image_url: width: 400 }}"
alt="{{ product.featured_image.alt | default: product.title }}"
>
<!-- Fallback for empty product vendor -->
<p>Brand: {{ product.vendor | default: 'Our Brand' }}</p>
<!-- Fallback for missing page content -->
<div>
{{ page.content | default: '<p>Content coming soon.</p>' }}
</div>
<!-- Combine default with other filters -->
{{ product.metafields.custom.subtitle | default: 'Great product' | upcase }}Next Steps
You now have a solid foundation in Shopify Liquid. You understand what it is, where it's used, and how the three building blocks — objects, tags, and filters — work together. Here's where to go next to deepen your knowledge:
- Shopify Liquid Cheat Sheet — A quick-reference guide with copy-paste code examples for the most common Liquid patterns. Keep it open while you code.
- Shopify Liquid Filters Reference — A complete walkthrough of every filter category: string, number, array, date, money, and more. Filters are where Liquid really shines.
- Shopify Liquid If/Else & Loops Guide — Deep dive into conditionals and iteration with practical, real-world examples. Master
if,unless,case/when,for, andtablerow.
Related Guides
Shopify Liquid Cheat Sheet
Quick-reference for tags, objects, and filters.
Shopify Liquid Filters
Complete reference of string, number, array, and date filters.
If/Else & Loops Guide
Master conditionals and iteration in Liquid templates.
Liquid Code Examples
15+ real-world Shopify Liquid code examples by use case.
We're building an AI Liquid code generator — join the waitlist.
Get early access when we launch. No spam, just one email.