Liquid Basics

Liquid is the templating language that powers CartOS themes. It lets you display dynamic content—product names, prices, collections, customer information—within your theme's HTML. If you want to customize your theme beyond what the visual editor offers, understanding Liquid basics opens up a lot of possibilities.

What is Liquid?

Liquid is a simple, readable templating language created by Shopify and used across many e-commerce platforms including CartOS. It sits between your theme's HTML and your store's data, inserting the right content in the right places.

When a customer visits your store, CartOS processes the Liquid code, replaces it with actual data, and sends plain HTML to their browser. Customers never see Liquid—they just see the final result.

Accessing theme code

To edit Liquid, go to Online Store › Themes, click the three dots on your theme, and select Edit Code. You'll see your theme's file structure with templates, sections, snippets, and assets.

Always duplicate your theme before editing code. This gives you a backup if something goes wrong.

Liquid syntax basics

Liquid uses two types of delimiters:

Output tags: {{ }}

Double curly braces output content to the page:

{{ product.title }}

This displays the product's title. Whatever is between the braces gets replaced with actual data.

Logic tags: {% %}

Curly brace with percent signs execute logic without outputting anything directly:

{% if product.available %}
  In Stock
{% else %}
  Sold Out
{% endif %}

This checks a condition and displays different content based on the result.

Objects and properties

Liquid gives you access to objects representing your store data. Objects have properties you access with a dot:

{{ product.title }}
{{ product.price }}
{{ product.description }}
{{ collection.title }}
{{ shop.name }}

Common objects

product – The current product on a product page

  • product.title – Product name
  • product.description – Full description
  • product.price – Price in cents (use filters to format)
  • product.images – Array of product images
  • product.variants – Array of variants
  • product.available – True if any variant is in stock
  • product.vendor – Product vendor
  • product.type – Product type
  • product.tags – Array of tags

collection – The current collection

  • collection.title – Collection name
  • collection.description – Collection description
  • collection.products – Array of products in the collection
  • collection.products_count – Number of products
  • collection.image – Collection image

cart – The customer's shopping cart

  • cart.items – Array of items in cart
  • cart.item_count – Number of items
  • cart.total_price – Cart total in cents

customer – The logged-in customer (if any)

  • customer.first_name – Customer's first name
  • customer.email – Customer's email
  • customer.orders – Array of past orders

shop – Your store

  • shop.name – Store name
  • shop.email – Store email
  • shop.currency – Store currency

Filters

Filters modify output. They're applied with a pipe character:

{{ product.title | upcase }}
{{ product.price | money }}
{{ product.description | truncate: 100 }}

Common filters

Text filters

  • upcase – Convert to uppercase
  • downcase – Convert to lowercase
  • capitalize – Capitalize first letter
  • truncate: 100 – Limit to 100 characters
  • strip_html – Remove HTML tags
  • escape – Escape special characters
  • newline_to_br – Convert newlines to <br> tags

Number and money filters

  • money – Format as currency ($10.00)
  • money_without_currency – Format without symbol (10.00)
  • plus: 5 – Add a number
  • minus: 5 – Subtract a number
  • times: 2 – Multiply
  • divided_by: 2 – Divide
  • round – Round to nearest integer

Date filters

  • date: "%B %d, %Y" – Format date (January 15, 2025)
  • date: "%m/%d/%y" – Format date (01/15/25)

Array filters

  • first – Get first item
  • last – Get last item
  • size – Get array length
  • sort – Sort alphabetically
  • reverse – Reverse order
  • join: ", " – Join array into string

Image filters

  • img_url: '500x' – Get image URL at specific size
  • img_url: 'master' – Get full-size image URL

You can chain multiple filters:

{{ product.title | upcase | truncate: 20 }}

Conditional logic

Use if, elsif, and else to show content conditionally:

{% if product.available %}
  <button>Add to Cart</button>
{% else %}
  <button disabled>Sold Out</button>
{% endif %}

Comparison operators

  • == – Equals
  • != – Not equals
  • > – Greater than
  • < – Less than
  • >= – Greater than or equal
  • <= – Less than or equal
  • contains – Check if string/array contains value
{% if product.price > 10000 %}
  Free shipping on this item!
{% endif %}

{% if product.tags contains 'sale' %}
  <span class="sale-badge">On Sale</span>
{% endif %}

Combining conditions

Use and / or to combine conditions:

{% if product.available and product.price < 5000 %}
  Great deal - in stock and under $50!
{% endif %}

{% if customer or cart.item_count > 0 %}
  Welcome back!
{% endif %}

Unless

unless is the opposite of if—it runs when the condition is false:

{% unless product.available %}
  This product is currently unavailable.
{% endunless %}

Loops

Use for to loop through arrays:

{% for product in collection.products %}
  <div class="product-card">
    <h3>{{ product.title }}</h3>
    <p>{{ product.price | money }}</p>
  </div>
{% endfor %}

Loop variables

Inside a loop, you have access to special variables:

  • forloop.index – Current iteration (1, 2, 3...)
  • forloop.index0 – Current iteration zero-based (0, 1, 2...)
  • forloop.first – True on first iteration
  • forloop.last – True on last iteration
  • forloop.length – Total number of iterations
{% for product in collection.products %}
  {% if forloop.first %}
    <p>Featured:</p>
  {% endif %}
  <h3>{{ forloop.index }}. {{ product.title }}</h3>
{% endfor %}

Limiting loops

{% for product in collection.products limit: 4 %}
  {# Only show first 4 products #}
{% endfor %}

{% for product in collection.products offset: 2 %}
  {# Skip first 2 products #}
{% endfor %}

Variables

Create variables with assign:

{% assign sale_price = product.price | times: 0.8 %}
<p>Sale: {{ sale_price | money }}</p>

Use capture to assign a block of content to a variable:

{% capture product_info %}
  {{ product.title }} - {{ product.price | money }}
{% endcapture %}

<meta name="description" content="{{ product_info }}">

Theme structure

CartOS themes are organized into several folders:

Layout

The layout folder contains your base templates. theme.liquid is the main layout wrapping all pages—it includes your <head>, header, footer, and a {{ content_for_layout }} tag where page content goes.

Templates

The templates folder has templates for different page types:

  • index.liquid – Homepage
  • product.liquid – Product pages
  • collection.liquid – Collection pages
  • cart.liquid – Cart page
  • page.liquid – Static pages
  • blog.liquid – Blog listing
  • article.liquid – Blog posts
  • customers/*.liquid – Account pages

Sections

Sections are reusable, customizable components. They can have their own settings that appear in the theme editor. The sections folder contains these modular pieces.

Snippets

Snippets are small, reusable code fragments. Include them with:

{% render 'product-card', product: product %}

This is useful for code you use in multiple places, like a product card component.

Assets

CSS, JavaScript, images, and fonts go in the assets folder. Reference them with:

{{ 'style.css' | asset_url }}
{{ 'logo.png' | asset_url }}

Common customizations

Show content based on tags

{% if product.tags contains 'new' %}
  <span class="badge new">New Arrival</span>
{% endif %}

{% if product.tags contains 'bestseller' %}
  <span class="badge bestseller">Best Seller</span>
{% endif %}

Show sale percentage

{% if product.compare_at_price > product.price %}
  {% assign discount = product.compare_at_price | minus: product.price %}
  {% assign percent = discount | times: 100.0 | divided_by: product.compare_at_price | round %}
  <span class="sale-percent">{{ percent }}% off</span>
{% endif %}

Display different content for logged-in customers

{% if customer %}
  <p>Welcome back, {{ customer.first_name }}!</p>
{% else %}
  <p><a href="/account/login">Log in</a> for exclusive offers.</p>
{% endif %}

Limit products per row

{% for product in collection.products %}
  <div class="product-card">
    {{ product.title }}
  </div>
  {% if forloop.index | modulo: 4 == 0 %}
    <div class="row-break"></div>
  {% endif %}
{% endfor %}

Debugging

When things don't work, these techniques help:

Output variable contents

<pre>{{ product | json }}</pre>

This shows the raw data structure, helping you understand what properties are available.

Check if something exists

{% if product.metafields.custom.size %}
  Size: {{ product.metafields.custom.size }}
{% else %}
  (no size metafield)
{% endif %}

Learning more

This covers the basics, but Liquid can do much more. For deeper learning:

  • Study your theme's existing code to see patterns
  • Make small changes and see what happens
  • Check the CartOS Liquid reference documentation for all available objects and filters
  • Look at how sections define their settings schema

If you're making significant theme changes, consider hiring a developer. Small tweaks are manageable with basic Liquid knowledge, but major customizations benefit from professional expertise.