# What Is Liquid, Shopify's Template Language

> What Liquid is, Shopify's template language: objects, tags and filters, how it writes your product page data on the server and how to check it.

URL: https://clione.ai/glossary/shopify-liquid/
Lang: en

Here Liquid has nothing to do with liquids or with Apple's Liquid Glass design: it is the language your Shopify store's theme is written in. Every time someone opens a [product page](/glossary/product-page/), a Liquid template decides which title, which price and which description appear in the HTML. It matters to you even if you will never program, because **data you store in Shopify only reaches the page if a line of Liquid prints it**. If the line is not there, the data exists in the admin and nobody sees it on the store, neither people nor search engines.

## How it works

Shopify defines Liquid as a template language created by Shopify itself, available as an open-source project on GitHub and used by many other projects and companies. It lets you write a single template with the fixed content and insert the information that changes depending on where it is rendered. Besides themes, Shopify uses variants of Liquid in notification templates, in Shopify Flow and in packing slip and order printing templates.[^shopify-liquid-ref]

The repository explains why it was designed that way: so that people who use an application can edit its look **without running unsafe code on the server**. Liquid does not evaluate arbitrary code, the templates are made for users to edit and it works as well for HTML as for emails. It is a Ruby library with an MIT license.[^liquid-github]

All of Liquid rests on three pieces:[^shopify-liquid-ref]

- **Objects**, in double curly braces, which print a piece of data: `{{ product.title }}` writes the product's title.
- **Tags**, between curly brace and percent sign, which define the logic: `{% if %}`, `{% for %}` and company decide what gets printed and how many times.
- **Filters**, after a vertical bar, which transform a value before printing it: `{{ product.price | money }}` gives the price the currency format the store has configured.[^money-filter]

Liquid handles six data types: string, number, boolean, `nil`, array and `empty`. Two rules explain many store errors. The first: `nil` is an undefined value that **prints nothing**, so missing data does not break the page, it simply does not appear. The second: in a condition **only `nil` and `false` are falsy**; an empty string, a zero or an empty array count as truthy.[^liquid-basics] Each resource also has a _handle_, a lowercase identifier generated from its title that is used in URLs and to look the resource up from the template.[^liquid-basics]

## Where it lives in the theme

A Shopify theme is organized in folders: `layout`, with the `theme.liquid` file that wraps all pages; `templates`, one per page type, such as the product one; `sections` and `blocks`, the modules the merchant adds and arranges from the editor; `snippets`, reusable pieces of Liquid; and `assets`, `config` and `locales`. JSON templates only wrap sections, and **the Liquid code is in the sections, the blocks and the _snippets_**.[^theme-architecture] If you are looking for which line prints a piece of product page data, it is almost always in a product section, not in the template.

Liquid is resolved on the server. Shopify recommends painting the initial content with Liquid and HTML instead of with JavaScript frameworks in the browser, so that **the initial HTML works without JavaScript** and the browser can show the content as soon as it receives it.[^shopify-render-server] That is [server-side rendering](/glossary/server-side-rendering/): what Liquid prints travels in the first response, without waiting for [JavaScript rendering](/glossary/javascript-rendering/), and a [crawler](/glossary/web-crawler/) that does not run scripts reads it too.

## On a store

**Fictional example.** A clothing store wants a T-shirt's product page to show the title, the price, the care instructions it keeps in a [metafield](/glossary/shopify-metafields/) and the product markup in [JSON-LD](/glossary/json-ld/). The theme's product section handles it with these lines:

```liquid
<h1>{{ product.title }}</h1>
<p class="price">{{ product.price | money }}</p>
{% if product.metafields.custom.care_instructions %}
  <p class="care">{{ product.metafields.custom.care_instructions.value }}</p>
{% endif %}
<script type="application/ld+json">
  {{ product | structured_data }}
</script>
```

And this is what the browser receives, already written, with no curly brace in sight:

```html
<h1>Linen T-shirt</h1>
<p class="price">$29.00</p>
<p class="care">Machine wash cold. Do not tumble dry.</p>
```

The last part deserves a line. The `structured_data` filter turns the product object into [Schema.org](/glossary/schema-org/) [structured data](/glossary/structured-data/): a [Product](/glossary/product-schema/) if it has no variants and a [ProductGroup](/glossary/productgroup/) if it has one or more.[^structured-data-filter] So the JSON-LD of many themes is written by nobody by hand: Liquid generates it from the data the product has, and only from that.


Checking that a piece of data "exists" with an `{% if %}` does not protect against an empty field. In Liquid only `nil` and `false` are falsy: **an empty string counts as truthy**,[^liquid-basics] so the condition passes and the product page prints a label with no content. For data that truly does not exist, on the other hand, no condition is needed to avoid an error: `nil` prints nothing.


## How to check it

1. **Open the theme code** and go to the `sections` folder: look for the section that the product template uses. The JSON template only tells you which sections it loads; the Liquid is inside them.[^theme-architecture]
2. **Search for the data by name.** For a metafield, search for its namespace and key (for example, `custom.care_instructions`). If it does not appear in any theme file, the theme does not print it, unless a block connects it from the editor or an app paints it with a [Shopify app embed](/glossary/shopify-app-embed/).
3. **Look at the result in the product page's source code**, not in the inspector. What Liquid prints appears already written; if a value shows up only in the inspector, JavaScript added it afterward.
4. **Search for `application/ld+json`** in that source code and count how many blocks there are. If the theme uses the `structured_data` filter and an app also adds its own markup, there may be two descriptions of the same product.
5. **Try an incomplete product**, with no description or without the metafield, and see what the template leaves: nothing, an empty label or a default text.

## Related concept: Stencil

Liquid is Shopify's template language; [Stencil](/glossary/bigcommerce-stencil/) is BigCommerce's theme engine, based on Handlebars templates.[^bigcommerce-stencil] They do the same job, turning catalog data into each page's HTML, with different syntax, so a snippet written for a Shopify theme does not work as is in a BigCommerce one. Within Shopify, Liquid is the piece that prints what [metafields](/glossary/shopify-metafields/) and [metaobjects](/glossary/shopify-metaobject/) store.

To keep going, the guide [from metafield to published page](/guides/metafield-to-published-page/) follows a piece of Shopify data all the way to the product page's HTML, and the [publishing by platform reading path](/learn/publishing-by-platform/) puts the rest of the terms in order.
