Looping in Liquid

Introduction

In this tutorial taking an in-depth look at all the options available to us when we loop over arrays.

On our demo Bakery Store site we have cupcake.html which lists all the cupcakes available:

---
layout: page
title: Muffins
---
<h1>Our cupcakes</h1>

<div class="cupcakes">
  {% for cupcake in site.cupcakes %}
    <div class="cupcake">
      <div class="image">
        <img src="{{ cupcake.image_path }}" alt="{{ cupcake.type }}" />
      </div>
      <h2>{{ cupcake.type }}</h2>
      <p>{{ cupcake.description }}</p>
    </div>
  {% endfor %}
</div>

Cupcakes

Cycle

First we’ll add an image filter in CSS to make all the images black and white:

...
<img src="{{ cupcake.image_path }}" alt="{{ cupcake.type }}" style="-webkit-filter: grayscale(100%)" />
...

Black and white cupcakes

Let’s change the image filter for each image. We’ll make the first image black and white, the second sepia, the third inverted and then cycle through those options for the rest of the images. To do this we’ll use a cycle. A cycle has values it iterates over each time it’s called:

...
<img src="{{ cupcake.image_path }}" alt="{{ cupcake.type }}" style="-webkit-filter: {% cycle "grayscale", "sepia", "invert" %}(100%)" />
...

Cycle Cupcakes

Index

Next we’ll number the cupcakes. We can access the current iteration of the loop using forloop.index:

...
<h2>{{ forloop.index }}. {{ cupcake.type }}</h2>
...

Cupcakes Index

We can make the 0 indexed if we use forloop.index0:

...
<h2>{{ forloop.index0 }}. {{ cupcake.type }}</h2>
...

Cupcakes Index0

First and last

Let’s change the first and last cupcakes so the title is white text on a black background. We can check it’s the first or last item in the loop using forloop.first and forloop.last:

...
<h2
{% if forloop.first or forloop.last %}
  style="background: black; color: white;"
{% endif %}
>{{ forloop.index0 }}. {{ cupcake.type }}</h2>
...

Cupcakes First

Rindex

If we want to change the highlighting to the last three items instead of the first and the last we could use forloop.rindex. rindex gives us how many iterations are left in the loop. So on the first iteration the forloop.rindex will be six, on the second it would be five and on the last it would be one. We can also use forloop.rindex0 to have a zero indexed number:

...
<h2
  {% if forloop.rindex <= 3 %}
    style="background: black; color: white;"
  {% endif %}
>{{ forloop.index0 }}. {{ cupcake.type }}</h2>
...

Cupcakes rindex

Length

When you hover over a heading on the cupcakes page, let’s make it popup with the total number of cupcakes available. I could get the size from the collection using site.cupcakes.size or we could get it from the forloop using forloop.length:

...
<h2
{% if forloop.rindex <= 3 %}
  style="background: black; color: white;"
{% endif %}

title="{{ forloop.length }} cupcakes"
>{{ forloop.index0 }}. {{ cupcake.type }}</h2>
...

Cupcakes title

Continue

We can use a continue to skip straight to the next item in the loop. In this example we’ve skipped over the Lemon cupcake:

...
{% for cupcake in site.cupcakes %}
  {% if cupcake.type == "Lemon" %}
    {% continue %}
  {% endif %}
  <div class="cupcake">
    <div class="image">
      <img src="{{ cupcake.image_path }}" alt="{{ cupcake.type }}"
        style="-webkit-filter: {% cycle "grayscale", "sepia", "invert" %}(100%)"
      />
    </div>
    <h2
      {% if forloop.rindex <= 3 %}
        style="background: black; color: white;"
      {% endif %}

      title="{{ forloop.length }} cupcakes"
    >{{ forloop.index0 }}. {{ cupcake.type }}</h2>
    <p>{{ cupcake.description }}</p>
  </div>
{% endfor %}
...

Cupcakes continue

Break

We can use break to exit the loop. In this example we’re dropping out of the loop when it gets to the Lemon cupcake:

...
{% for cupcake in site.cupcakes %}
  {% if cupcake.type == "Lemon" %}
    {% break %}
  {% endif %}
  <div class="cupcake">
    <div class="image">
      <img src="{{ cupcake.image_path }}" alt="{{ cupcake.type }}"
        style="-webkit-filter: {% cycle "grayscale", "sepia", "invert" %}(100%)"
      />
    </div>
    <h2
      {% if forloop.rindex <= 3 %}
        style="background: black; color: white;"
      {% endif %}

      title="{{ forloop.length }} cupcakes"
    >{{ forloop.index0 }}. {{ cupcake.type }}</h2>
    <p>{{ cupcake.description }}</p>
  </div>
{% endfor %}
...

Cupcakes break

Reversed

Let’s get rid of the break statement and try reversing the order of the loop. We can do this by passing reversed to the for loop:

...
{% for cupcake in site.cupcakes reversed %}
...

Cupcakes reversed

Limit and offset

We can add a limit to a show maximum of three cupcakes and an offset which skips over the first x number of cupcakes:

...
{% for cupcake in site.cupcakes reversed limit: 3 offset: 3 %}
...

Cupcakes offset

Else

You might need to handle the case where your array is empty. In this example we’ll create an empty flavours array, then we’ll loop over that array and specify an else which is the case where the array is empty:

---
layout: page
title: Muffins
flavours: []
---
...
{% for flavour in page.flavours %}
  {{ flavour }}
{% else %}
  <h3>There are no flavours</h3>
{% endfor %}
...

Cupcakes else