tutorials

The logic of Liquid - an introduction to the templating language

I summarise the logical functions embodied in Liquid that enable you to bring dynamic features to a Jekyll site or Shopify storefront.

A vector drawing of Thomas Bishop's bearded and bespectacled face
Thomas
20 Jan 2019 · Liquid, Jekyll, Shopify

Introduction

Liquid is a simple markup language, used to build templates from static site files, similar to Mustache and JSON-T (Squarespace's tempating framework). The static site generator Jekyll uses Liquid to simplify the creation of dynamic-style features without a CMS or server preprocessing language like PHP. It was originally developed by Shopify to create their e-commerce CMS and is also used by the preeminent CRM tool Salesforce.

This short guide, is written from the point of Jekyll development but most of the techniques are easily transferable to other Liquid contexts.

Variables

As with more advanced languages, a variable is a container for storing data . Within Shopify’s Liquid documentation, they call variables objects. (We will stick with ‘variable’ as it is a more indicative name, especially if you are coming at this with a background in other programming languages.)

In Liquid the data stored can include strings, numbers and Booleans. Within Jekyll, we declare our variables within the front matter region at the top of our page’s document. Once you have declared your variable you are then free to call it within the page or elsewhere within your file system.

This site is built with Jekyll. Here is this article's front matter.

Shopify has its own ecosystem of variables related to e-commerce entities such as products, product collections, prices etc. Most of these are not much use within Jekyll since they pertain to the specifics of Shopify. In Jekyll you are mostly going to be working a limited series of components: primarily pages and posts.

The variable syntax is as follows: {{ variable }} The whitespace either side of the string matters. If you don't include it, you will end up with a syntax error!

In the following example we declare a variable for the H1 HTML element and then call it within the markup:

Note that we cannot just insert {{ h1 }} it is necessary to prepend the variable with the document type context: the page.

Variable filters

You can change the output of a given variable by running it through a filter. You append the filter with a pipe. The following changes the output of our H1 to uppercase: {{ page.h1 | upcase }}

Another example of a filter: {{ page.date | date: “%-d %b %Y” }} sets a particular format for a date. And also {{ page.h1 | truncatewords:3 }} which is useful when creating snippets of content like in a blog preview.

You can also apply mulitiple filters to the same variable:

Logical operations using tags

In addition to variables, the other main components in Liquid are tags. Tags, combined with variables give the language its full templating power. Tags are applied via the following syntax:

{% insert_tag %}

Within Liquid, as devised for Shopify, there are four tag types which allow you to integrate fairly complex logic. In the remainder of this tutorial we will focus on the tags which prove most useful when building sites with Jekyll and which have the widest applicability. You can consult the Liquid documentation if you wish to dig deeper or if you are seeking a functionality not covered here.

Tag types

control flow

create conditions that decide whether blocks of code get executed. Includes the operators if, unless, else/elseif, case/when, and/or

iteration

repeatedly run blocks of code as loops

theme

primarily used to output code snippets or partials within the context of an integrated piece of code or document

variable

create variables on the hoof with operators like assign and capture. Within Jekyll, we tend to create variables within the front matter not in the course of our HTML so we will not be going into this class of tag here

Control flow: If statements

You are able to create logical functions that test for conditions with the schemas:

  • If P obtains execute Q
  • If P obtains execute Q, otherwise R

In order to implement conditional functions you obviously need to make use of a Boolean (i.e an assignment of truth or falsity). Here’s an example:

So here we have assigned ‘true’ to the variable show_heading. This means the heading will output the variable contained in h1. If we switched the Boolean to false it would not.

Note here that we don’t bother with ‘if true then...’, it is implied that it obtains by the ‘if’ statement. Liquid is nice and concise.

That was a basic example of if, then logic however you can also build up more complex logical chains which check for multiple truth conditions. Here is an example scenario:

If show_heading is true, output h1, if false, check if heading contains ‘Liquid’. If so, output h1. If not, do not print heading.

Here is this logic output as Liquid code:

Some points on the syntax of conditional logical statements:

  • The conditional sequence (regardless of length) must start with if and end with endif

  • contains is a tag that has a special assignation of meaning within Liquid. This term hasn’t just been used arbitrarily

  • When including additional if outcomes, they must be triggered by the operator elseif.

  • The first additional if statement, after if itself can follow elseif. After this, all subsequent if statements must be preceded by else.

  • I’m not sure of the convention (and often Liquid code will run regardless) but as much as possible, I like to nest the HTML inside the liquid tags that operate on it. This makes it easier to read and helps you discern where the Liquid functions begin and end.

Iteration: loops with arrays

You are also able to execute loop functions within Liquid, facilitated by iteration tags.

In order to create a loop, you need to first establish an array which will hold the variables we will loop over. The array is stored in the front matter.

First example: a descriptive list

Let’s say we want to make a list out of a series of terms which we are going to define. The most appropriate HTML element for this is a descriptive list. So we are going to create an array within the front matter of the terms we want to define and then create an iterative loop in our document that pulls them in as variables:

Because we have created a loop, we do not need to add additional markup for Term 2 and Term 3, Liquid simply replicates the markup for the first term for each subsequent term, until it has worked its way through the whole array. I point this out because it took me a while to grasp.

To clarify the syntax:

  • We always start the loop with for and end with endfor. for is the tag triggers signals the loop

  • The Liquid logic must wrap the HTML parent element and as proximately as possible

  • We define a keyword with the for statement - in our example it is term Again this is not an arbitrary use of natural language, it is a means by which to gain a hold on the variables being worked on by the loop function

Second example: a summary list of blog posts

A common use of loops within Jekyll is for blog pages, where you want to output a summary of your various blogs. We are going to create a loop that pulls in a blog’s title, summary and date of publication.

To do this we do not need to create an array. Arrays aren't always necessary for a loop. Instead we will just use a series of string variables. Importantly, as we are pulling content from multiple blog posts, the key information will be contained in files other than our blog summary template. We are working with the assumption that each blog has the following variables in its front matter, saved within a _posts directory in your sites file structure:

A very helpful parameter

By default, an iterative loop will execute until all of its values have been output. However you may not want all of the values to be outputted. A handy way to get around this is to insert limit: (number of pulls). When you do this and specify a numerical value (say, 3) only three of the values stored in the array will be pulled. In the context of our blog example: {% for post in site.posts limit: 3 %}.

Within Liquid this is known as a ‘parameter’ however in terms of function, it acts just like a filter only you don’t use a pipe to signal its usage. There are several other parameters you can add which control behaviour at different intervals of the loop, see Liquid iteration tags for further detail.

Apply conditional logic to iterative loops

You can use control flow logic within the context of loops. The most useful application for my purposes has been {% else %} which covers you and allows you to specify a certain output (e.g ‘null’) if a value from an array cannot be found.

Theme tags

Liquid provides a bunch of pre-defined tags specific to Shopify’s e-commerce focus. These pertain to the creation of code snippets that can be reused throughout the platform.

Jekyll has its own variation on this that will be familiar to anyone who has ever built a site using Jekyll. I will briefly summarise how theming works in Jekyll since it is something that is made possible by Liquid.

In Jekyll, we store reusable HTML or Markdown (‘partials’) within the _includes directory. This site is built using Jekyll. This is what my _includes looks like:

My _includes directory

We can call any file within the _includes directory by using the special Jekyll tag {% include.filename.html %}.

When called via a theme tag these partials combine to build the structure of my site’s pages. This functionality effectively recreates in static form the PHP hooks that exist within sites built with dynamic server-side content rendering (Drupal, WordPress, Joomla, and so on).

There are two ways that we make use of partials in Jekyll, viz.

  • layouts

  • inline

Layout theme tags

Layouts are templates for a specific page type. Most Jekyll sites will have at least two layouts stored within the _layouts directory: a ‘default’ layout for static (non-chronological) pages and a ‘post layout’ for blog posts (chronological content).

For example, here is my default.html template, contained within _layouts:

This template comprises a series of theme tags (such as {% include.header.html %}, which pulls content from the _includes directory into the structural HTML tags which is then compiled by Jekyll when we apply jekyll build . Note that they are syntactically indistinguishable from loop and control flow tags, underscoring the point that ‘theme tags’ are more of a notional distinction in Jekyll when compared with Shopify.

Inline theme tags

You are not limited to using theme tags within layouts however. You can also insert them within specific pages that are instances of a given layout. For example, within my home page which instantiates the default.html layout and which is contained within the {% content %} variable in the above code, I could pull in another partial such as photo-gallery.html.

Glossary of Liquid filters

Default Shopify filters

The following is a list of Liquid filters which have proved most useful for me. This is therefore not a definitive list. For the full list of available filters see: https://shopify.github.io/liquid/filters/.

Filter Function Example
append concatenates two strings and returns the concatenated value. (Do not confuse with 'concat' and 'concatenate' below.) {{ 'this/is/a/url | append '.html' }}
capitalize makes the first character of a string capitalized. Variations: 'downcase', 'upcase' {{ 'title' | capitalize }}
concatenate joins together multiple arrays into one. See also 'join' {{ assign everything = array1 | concat: array2 }}
default specify a fallback in case a value for a variable doesn’t exist {{ product_price | default: 2.99 }}
join outputs the contents of an array with the values separated by a specified word, e.g 'and' {{ array-name | join: 'and' }}
first, last output either the first or last outputs in an array {{ array-name.first }} {{ array-name.last }}
map creates an array of values by extracting the values of a named property from an-other object See Liquid: map
prepend adds the specified string to the beginning of another string {{ 'apples, oranges, and bananas' | prepend: 'Some fruit:'' }}
replace replace every occurence of a substring with another string {{ 'here is a sentence' | replace 'a','the' }}
strip_html removes any HTML tags from a string {{ <p>Some HTML</p> | strip_html }}
truncate shorten a string down to the number of characters passed as a parameter (variation: 'truncate_words') {{ 'A sentence' | truncate:5 }}

Jekyll filters

Jekyll also includes some of its own filters, for functionality that goes beyond the Shopify defaults. Particularly useful are the following:

Filter Function Example
absolute url outputs a full URL path to the built site, when the input is a relative URL. Helps you achieve the best of both worlds - relative URLs for simplicity and easeduring development, absolute URLs for the finished site for SEO purposes {{ 'a/url.css' | relative_url }}
date to string takes Jekyll’s default date format and outputs in a more readable form asa string {{ site.time | date_to_string }}
number of words counts the number of words in a given content variable {{ page.content | number_of_words }}

Category: tutorials Tag(s): Liquid, Jekyll, Shopify