Liquid Tag Reference

From Spiffy Stores Knowledge Base

Liquid Tags provide the logic and control flow in your template. They allow you to display data conditionally and to dynamically change the way the template is rendered according to the data in the Liquid variables.

Comment

A comment is the simplest of tags. It just swallows content.

Hi fred {% comment %} you stink {% endcomment %}

Echo

Output an expression in the rendered HTML. This is identical to wrapping an expression in {{ and }}, but works inside liquid tags and supports Filters.

{% liquid
if product.featured_image
  echo product.featured_image | img_tag
else
  echo 'product-1' | placeholder_svg_tag
endif %}

Liquid

The liquid tag allows you to write multiple tags within a single set of delimiters.

{% liquid
  case section.blocks.size
  when 1
    assign column_size = ''
  when 2
    assign column_size = 'one-half'
  when 3
    assign column_size = 'one-third'
  else
    assign column_size = 'one-quarter'
  endcase %}

Use the echo tag to output an expression within a {% liquid %} tag.

Variable Assignment

You can store data in your own variables, to be used in output or other tags as desired.

The simplest way to create a variable is with the assign tag, which has a pretty straightforward syntax:

{% assign name = 'freestyle' %}
{% for t in collections.tags %}{% if t == name %}
  <p>Freestyle!</p>
{% endif %}{% endfor %}

Another way of doing this would be to assign true/false values to the variable:

{% assign freestyle = false %}
{% for t in collections.tags %}{% if t == 'freestyle' %}
  {% assign freestyle = true %}
{% endif %}{% endfor %}
{% if freestyle %}
  <p>Freestyle!</p>
{% endif %}

If you want to combine a number of strings into a single string and save it to a variable, you can do that with the capture tag. This tag is a block which "captures" whatever is rendered inside it and assigns it to the given variable instead of rendering it to the screen. Here's how it works:

  {% capture attribute_name %}{{ item.title }}-{{ i }}-color{% endcapture %}

  <label for="{{ attribute_name }}">Colour:</label>
  <select name="attributes[{{ attribute_name }}]" id="{{ attribute_name }}">
    <option value="red">Red</option>
    <option value="green">Green</option>
    <option value="blue">Blue</option>
  </select>

Arrays

If a liquid expression returns an array of objects, then you can access the members of the array using an index notation.

For example to access the third element of an array of products

{{ collection.products[2].title }}

All array elements are accessed using a zero-based index, so the first element of the array is always index 0.

Additionally, three special methods are available for arrays.

'size' - Returns the number of elements in the array
'first' - Returns the first element in the array
'last' - Returns the last element in the array
{{ collection.products.first }}
{{ collection.products.last }}
You have {{ cart.items.size }} items in your cart

Raw Tags

If you need to include code in your template that you don't want interpreted by the Liquid parser, then you need to enclose it with raw tags.

These tags will allow you to incorporate code such as JavaScript templates that use the same curly braces as Liquid.

{% raw %}{{ 3 | plus: 4}}{% endraw %} is usually equal to 7.

The code between the raw tags is protected, so the result is

{{ 3 | plus: 4}} is usually equal to 7.

If / Else

The conditional expressions formed with if/else should be a familiar construction from most programming languages. Liquid allows you to write simple expressions in the if.

The conditions that can be used to test for logical conditions are:

'==' - Equal
'!=' - Not Equal
'<>' - Not Equal
'<' - Less Than
'>' - Greater Than
'>=' - Greater Than or Equal
'<=' - Less Than or Equal
'contains' - String contains substring, or Array contains element

You can perform comparisons against a number of special literals:

'nil' - The object does not exist
'null' - The object does not exist
'true' - The object is true
'false' - The object is false
'blank' - The object is a blank string
'empty' - The object (array or string) is empty

Compound expressions may be formed by the use of or and and expressions.

{% if user %}
  Hi {{ user.name }}
{% endif %}

{% if user.name == 'fred' %}
  hi fred
{% endif %}

{% if user.name != 'fred' %}
  you aren't fred
{% endif %}

{% if user.name == 'fred' or user.name == 'dirk' %}
  hi dirk or fred
{% endif %}

{% if user.name == 'fred' and user.last_name == 'bloggs' %}
  hi fred bloggs
{% endif %}

{% if user.creditcard == null %}
  user has no credit
{% endif %}

{% if user.payments == empty %}
  you never paid !
{% endif %}

{% if user.age > 18 %}
  Login here
{% else %}
  Sorry, you are too young
{% endif %}

{% if user.name contains 'y' %}
  Sorry, we don't allow users with a 'y' in their name!  
{% endif %}

Multiple if statements can be chained together using if/elsif/else statements. For example,

{% if product.price > 1000 %}
  Free shipping
{% elsif product.price == 1000 %}
  Half-price shipping
{% elsif product.price > 500 %}
  Bonus gift
{% else %}  
  Shipping is $10
{% endif %}

Unless / Else

The conditional expressions formed with Unless/Else are the reverse of the usual If/Else logic. An Unless/Else block is written using exactly the same expressions and conditions as an If/Else block, but the test logic is reversed.

{% unless user.name == 'fred' %}
  you aren't fred
{% endunless %}

{% unless user.age > 18 %}
  Sorry, you are too young
{% else %}
  Login here
{% endunless %}

IfChanged Block

The ifchanged block tag is used within a loop. It checks its own rendered contents against its previous state and only displays its content if the value has changed.

For example, in order to show the date only when a post is published on a new day (always displaying the time):

{% ifchanged %}
  <div class="date">{{ article.published_at | date: '%B %d'  }}</div>
{% endifchanged %}
<div class="time">{{ article.published_at | date: '%I:%M %p'  }}</div>

Case Statement

If you need more than one condition you can use the case statement. Multiple when conditions may be included by separating each condition by an or keyword or a comma.

{% case line_item.quantity %}
  {% when 0 %}
  none
  {% when 1 %}
  one
  {% when 2 %}
  two
  {% when 3, 4 %}
  either 3 or 4
  {% when 5 or 6 or 7 %}
  either 5, 6 or 7
  {% else %}
  a few more...
{% endcase %}

Example:

{% case template %}
	
{% when 'label' %}
     // {{ label.title }}
{% when 'product' %}
     // {{ product.vendor | link_to_vendor }} / {{ product.title }}
{% else %}
     // {{page_title}
{% endcase %}

Cycle

Often you have to cycle through an number of alternative values. Liquid has built-in support for such operations using the cycle tag.

{% cycle 'one', 'two', 'three' %}
{% cycle 'one', 'two', 'three' %}
{% cycle 'one', 'two', 'three' %}
{% cycle 'one', 'two', 'three' %}

This produces

one
two
three
one

If no name is supplied for the cycle group then its assumed that multiple calls with the same parameters are one group.

If you want to have total control over cycle groups you can optionally specify the name of the group. This can even be a variable.

{% cycle 'group 1': 'one', 'two', 'three' %}
{% cycle 'group 1': 'one', 'two', 'three' %}
{% cycle 'group 2': 'one', 'two', 'three' %}
{% cycle 'group 2': 'one', 'two', 'three' %}

This produces

one
two
one
two

Increment

Create a new number variable, and increases its value by 1 every time increment is called on the variable. The counter's initial value is 0.

Here, an increment counter is used to create a unique numbered class for each list item.

<ul>
  <li class="item-{% increment counter %}">apples</li>
  <li class="item-{% increment counter %}">oranges</li>
  <li class="item-{% increment counter %}">peaches</li>
  <li class="item-{% increment counter %}">plums</li>
</ul>

The output from this is

<ul>
  <li class="item-0">apples</li>
  <li class="item-1">oranges</li>
  <li class="item-2">peaches</li>
  <li class="item-3">plums</li>
</ul>

Variables created using increment are separate from variables created using assign or capture.

In the example below, a variable named my_number is created using assign. The increment tag is then used several times on a variable with the same name. Note that the increment tag does not affect the value of my_number that was created through assign.

{% assign my_number = 10 %}

{% increment my_number %}
{% increment my_number %}
{% increment my_number %}

{{ my_number }}

The output generated is

0
1
2

10

Decrement

Create a new number variable, and decreases its value by 1 every time decrement is called on the variable. The counter's initial value is -1.

{% decrement variable %}
{% decrement variable %}
{% decrement variable %}

This produces

-1
-2
-3

Like increment, variables declared using decrement are independent from variables created using assign or capture.

For Loops

Liquid allows for loops over collections. This allows you to loop over things like line-items in an order.

  {% for item in order.line-items %}
    {{ item.description }}
  {% endfor %}

If the collection is empty, then you can use an optional else tag to provide alternative output.

  {% for item in order.line-items %}
    {{ item.description }}
  {% else %}
    <p>This collection is empty!</p>
  {% endfor %}

The order of the loop may be reversed by adding the flag reversed.

  {% for item in order.line-items reversed %}
    {{ item.description }}
  {% endfor %}

During every for loop, the following helper variables are available for additional control over the results.

 forloop.length     # => length of the entire for loop
 forloop.index      # => index of the current iteration
 forloop.index0     # => index of the current iteration (zero based)
 forloop.rindex     # => how many items are still left?
 forloop.rindex0    # => how many items are still left? (zero based)
 forloop.odd        # => is this iteration odd (zero based)?
 forloop.even       # => is this iteration even (zero based)?
 forloop.first      # => is this the first iteration?
 forloop.last       # => is this the last iteration?

There are several attributes you can use to influence which items you receive in your loop

  • limit lets you restrict how many items you get
  • offset lets you start the collection with the nth item.
  # array = [1,2,3,4,5,6]
  {% for item in array limit:2 offset:2 %}
    {{ item }}
  {% endfor %}
  # results in 3,4

Instead of looping over an existing collection, you can define a range of numbers to loop through. The range can be defined by both literal and variable numbers:

  # if item.quantity is 4...
  {% for i in (1..item.quantity) %}
    {{ i }}
  {% endfor %}
  # results in 1,2,3,4

The number of times that you loop through a for loop can be changed by using the break and continue statements.

You can leave a for loop entirely by using break.

{% for item in collection %}
  {% if item.condition %}
    {% break %}
  {% endif %}
{% endfor %}

You can stop processing a particular iteration of the loop, and continue processing the next iteration with continue.

{% for item in collection %}
  {% if item.other_condition %}
    {% continue %}
  {% endif %}
{% endfor %}

Tables

Liquid can also create table rows and cells for you, although you still need to wrap a table tag around the tablerow tag.

  <table>
    {% tablerow item in items cols: 3 limit: 12 %}
      {{ item.variable }}
    {% endtablerow %}
  </table>

You can also find out whether a table cell is the first or last column in a row or directly query the column number.

 tablerowloop.length       # => length of the entire for loop
 tablerowloop.index        # => index of the current iteration
 tablerowloop.index0       # => index of the current iteration (zero based)
 tablerowloop.rindex       # => how many items are still left?
 tablerowloop.rindex0      # => how many items are still left? (zero based)
 tablerowloop.first        # => is this the first iteration?
 tablerowloop.last         # => is this the last iteration?
 tablerowloop.odd          # => is this iteration odd (zero based)?
 tablerowloop.even         # => is this iteration even (zero based)?
 tablerowloop.col          # => index of column in the current row
 tablerowloop.col0         # => index of column in the current row (zero based)
 tablerowloop.col_first    # => is this the first column in the row?
 tablerowloop.col_last     # => is this the last column in the row?
  {% tablerow item in items cols: 3 %}
    {% if col_first %}
      First column: {{ item.variable }}
    {% else %}
      Different column: {{ item.variable }}
    {% endif %}
  {% endtablerow %}

Special Literals

We've already seen a number of special literals that can be used in comparison operations. The full list of special lierals is a follows.

  • 'nil'
  • 'null'
  • 'true'
  • 'false'
  • 'blank'
  • 'empty'
  • 'now'

The 'now' literal can be used with Date Filters to reference the current date and time. The date and time is always returned using the time zone of the store, as specified in the Toolbox Preferences -> Standards & formats.

{% capture day_name %}{{ 'now' | date: "%A" }}{% endcapture %} 
{% capture current_time %}{{ 'now' | date: "%H:%M" }}{% endcapture %}
<p>Today is {{ day_name }}, and the time is {{ current_time }}.</p>

=>

Today is Sunday, and the time is 13:09.

Including Snippets

Liquid code can include code fragments or snippets from the theme's snippets folder or from the global snippets library. The theme's snippets folder is searched first before a search is made of the global library. This means that you can use any of the snippets from the global library, but override the snippet by creating a snippet of the same name in the theme's snippet folder.

Details of all the global snippets can be found here: Using global Liquid snippets.

These snippets can be used to include code in a layout, template or another snippet.

All snippets are named as follows

_snippet_name.liquid

A snippet is included into Liquid code by using the include tag.

{% include 'snippet_name' %}

Note that the leading '_' character is not required on the include tag.

When a snippet is included into another piece of Liquid code, it will be rendered using all of the currently assigned variables. An optional with parameter allows you to assign a value to a variable bound to the snippet's name within the snippet's context.

For example, assume we have a snippet called _widget.liquid.

<p>The widget shape is {{ widget }}. It is {{ colour }}.</p>

Within a template we can include the following code:

{% assign colour = 'yellow' %}
{% include 'widget' %}
{% include 'widget' with 'round' %}
{% include 'widget' with 'square' %}
{% assign colour = 'green' %}
{% include 'widget' with 'triangle' %}

This will render as

<p>The widget shape is . It is yellow.</p>
<p>The widget shape is round. It is yellow.</p>
<p>The widget shape is square. It is yellow.</p>
<p>The widget shape is triangle. It is green.</p>

A snippet may also be included for use with a collection, with the snippet being rendered once for each member of the collection.

{% include 'product' for products %}

Each snippet is rendered with a local variable with the same name as the snippet being set to the value of each of the elements in the collection in turn. In the above example, the snippet is named product, and therefore a local variable named product is initialized with a value from the collection products each time the snippet is rendered.

Render

Use the render tag to render a snippet from the snippets folder of a theme. This tag replaces the deprecated include tag.

{% render 'snippet-name' %}

Note that you don't need to write the file's .liquid extension.

When a snippet is rendered, the code inside it does not automatically have access to the variables assigned using variable tags within the snippet's parent template. Similarly, variables assigned within the snippet can't be accessed by the code outside of the snippet. This encapsulation increases performance and helps make theme code easier to understand and maintain.

Note: When a snippet is rendered using the render tag, the deprecated include tag may not be used within the snippet.

Passing variables to a snippet

Variables assigned using variable tags can be passed to a snippet by listing them as parameters on the render tag.

{% assign my_variable = 'apples' %}
{% render 'name', my_variable: my_variable, my_other_variable: 'oranges' %}

Global objects don't need to be passed down. They are accessible from all files.

Caution: Assigning a variable within the snippet that is also assigned in the parent template does not overwrite its value in the parent template.

The with parameter

A single object can be passed to a snippet by using the with and as parameters:

{% assign featured_product = all_products['product_handle'] %}
{% render 'product' with featured_product as product %}

In the example above, the product variable in the snippet will hold the value of featured_product in the parent template.

The for parameter

A snippet can be rendered once for each value of an enumerable object by using the for and as parameters:

{% assign variants = product.variants %}
{% render 'variant' for variants as variant %}

In the example above, the snippet will be rendered once for each variant of the product, and the variant variable will hold a product variant object within the snippet.

When using the for parameter, the forloop object is accessible from within the snippet.

Specifying an Alternative Layout

By default, all Liquid templates are rendered using the theme.liquid layout file.

In some cases, it is useful to be able to select that a different layout file is to be used to render a particular template. The layout tag can achieve this.

The following tag can be inserted into a template to indicate that the layout file mobile.liquid should be used to render that template.

{% layout 'mobile' %}

If you don't want any layout to be used for a template, then use

{% layout 'none' %}

or

{% layout none %}

You may also specify the layout name using a variable, where the value of the variable is the name of the layout that you wish to use.

{% layout settings.customer_layout %}

You can include the layout tag within conditional logic tags to selectively use different layouts for the template file.

See our tutorial on Creating a custom landing page.

Creating Custom Forms

The form tag can be used to define a custom form in a page.liquid template. You must use the argument contact when defining the form.

{% form 'contact' %}
...
{% endform %}

A full description, with examples, on how to create custom forms can be found in the How to create and customize a Contact Form tutorial.

Further Reference