Liquid Tutorial 2 - Liquid variables and types

Written By Matt Jones

Last updated About 1 month ago

Introduction

In this tutorial, we’ll be looking more closely at how you can use Liquid to store and access information or data.

Creating Variables with the Assign Tag

As in most computer code languages, variables are an essential building block of Liquid. They allow you to store a piece of information temporarily, ready to be used later. The assign tag is a common way to create a new variable.

Example
{% assign my_number = 5 %} My number is {{my_number}}

One way to think of this is that we are creating a box with a label on it of “my_number” and we are putting the number 5 in the box. Later, we can access the box when we want to retrieve the contents.

Re-assigning Variables

The assign tag can also be used to re-assign variables to hold a new value. Think of this as keeping the same box, but taking out the original number, throwing it away, and replacing with the new number:

Example
{% assign my_number = 5 %} {% assign my_number = 7 %} My number is {{my_number}}

Types

Now let’s look at what types of data we can put inside a variable “box”. It’s useful to understand these as it can help to understand in future:

  • Which filters you can use and why some may not be working

  • Why certain variables may look the same, but logic is treating them as different

Looking at the next example, the two variables seem to be storing the same data, but there is a subtle difference:

Example
{% assign a = "1" %} {% assign b = 1 %}
  1. The first variable a, stores data with a String type, using quotes. In this variable, the character 1, is just an ordinary string of characters or words.

  2. The second variable stores data as an integer (whole number) type; there are no quotes around it. This tells Liquid that the contents of the variable will be a number and are ready for calculations.

Here are the full list of available Liquid types, with examples:

Type

Notes

Example 1

Example 2

String

A string of characters, which might make up single words, sentences or an API key for example.

“Hello World”

“15438543”

Integer

A whole number

1

65674

Float

Short for floating decimal point, this is a decimal number.

4.56

3.14

Boolean

A true or false value, useful in logic. Notice it does not use quotes and is a keyword.

true

false

Array

A list of values. The values themselves can have any of the types in this table, including nested arrays and objects.

[1,2,3]

[“apples”,”oranges”,”pears”]

Object

A representation of a complex entity with multiple properties. The values of the properties can have any of the types in this table, including nested arrays and objects.

{

“name”: “Luke Skywalker”
“category”: “jedi”
}

{

“name”: “URL”,

“slug”: “about”,

“slug2”: “liquid”

}

nil

A special keyword which when assigned to a variable, makes it as if the variable has never been created.

nil

null

A special keyword which assigned to a variable marks that variable as deliberately empty.

null

Checking Types

Sometimes you may need to check the type in a variable.

One way to check a type is with the | type_of filter.

Example
{% assign a = 3 %} {% assign b = "3" %} {{ a | type_of }} {{ b | type_of }}

Sometimes a quicker way to see both the type and the value of your variable at once is to use the | json filter. This converts the value into a JSON formatted String as it is output, which has the side-effect of adding quotes to Strings and formatting Objects and Arrays with curly braces and square brackets respectively:

Example
{% assign a = 3 %} {% assign b = "3" %} {{ a | json }} {{ b | json }}

We can here tell that a is a number and b is a string. We don’t for sure know if a is an integer or floating point number using this method.

Coercing or Changing Type

Most Liquid filters have an associated type as well. See the following examples:

Filter

Functionality

Associated Type

| upcase

Changes an entire String to uppercase characters

String

| plus

Carries out an addition operation on a number

Integer / Float

| array_uniq

Removes duplicate values from an array

Array

The platformOS filter docs will always document the intended type of a filter in the input bullet point:

So far, Liquid types work similarly to other languages like JavaScript, where certain methods work on Strings and others on Numbers.

However, Liquid is more unusual when it comes to changing types. There is no special filter deliberately designed to change type. If a filter is used on a variable with the wrong type, it will attempt to coerce the value to the correct type first, before carrying out the filter’s functionality. This is the way you’re intended to change the type of variables, though it can be tricky:

Example
{% assign number_string = "123" %} {% assign integer = number_string | plus: 0 %} {% assign array = number_string | split: "" %} {{number_string | json }} {{integer | json }} {{array | json }}

Note that we could also have used | plus: 1 on line 2 to change the type of the variable, but this would have had the side-effect of also changing the value. This side-effect may or may not be desired, depending on what you are aiming to do.

Only some types can be successfully coerced to other types; some combinations are impossible:

Example
{% assign a = "123" %} {% assign b = "Hello World" %} {% assign c = "123" | split: "" %} {% assign d = a | plus: 0 %} {{a | json }} => {{d | json }} {% assign e = b | plus: 0 %} {{b | json }} => {{e | json }} {% assign f = c | plus: 0 %} {{c | json }} => {{f | json }}

In this example, while the same filter was used on each line, only the first was successful at changing the type and keeping the value. You can see why Liquid might have an easier time converting “123” to a number than it does “Hello World”. The other values gave unexpected results of storing the added number 0 only and losing all trace of the original value. Sometimes you may instead get a Liquid error. When debugging, it’s very often useful to output the variable before and after the filter, and checking its type to see if an incorrect type caused the bug. Maybe a different filter is needed.

Why might you want to change types without changing the value? The main reason is to interact with other services, for example a Siteglide “include” tag, a GraphQL query or writing JSON to pass to JavaScript or an API call - these services expect a certain type and it’s important to change types to give them what they expect.

In summary, Liquid is what we call a loosely typed language. It is sometimes useful that you can use a filter without changing the type first, but it can produce bugs if you’re not aware of the possible kinds of data you’re working with.

Alternative ways to assign variables

So far we’ve looked at the assign tag to create variables, but you can also use the capture tag as a convenient way to create strings, especially when you want to concatenate characters from multiple sources together and preserve whitespace:

Example
{% assign message_start = "Welcome to the" %} {% assign location = "Jungle" %} {% capture full_message %}{{message_start}} {{location}}{% endcapture %} {{full_message}}

The parse_json filter and tag with the same name are also useful for creating objects and arrays, but we’ll return to these later.

Activity - Match the correct variables to the filters needed

Below in the example are a set of assigned variables followed by some outputs with muddled filters. Can you re-write the Liquid code so that the expected answers are given?

Leave the | json filter at the end of each output so that we can see the resulting types more clearly.

You can test out your code on an empty page in the Siteglide Admin, under CMS/Pages: Quickstart: Pages

Example
{% comment %}Variables{% endcomment %} {% assign string = "3" %} {% assign integer = 3 %} {% assign float = 0.5 %} {% assign boolean = false %} {% assign array = "1233" | split: "" %} {% comment %}Outputs{% endcomment %} 1) Keep value of the boolean but change type to string, expected answer: "false" => {{ boolean | times: 0.5 | json }} 2) Convert the number to a string, but keep its value unchanged, expected answer: "3" => {{ integer | array_uniq: | json }} 3) Convert the string to a number, but keep its value unchanged, expected answer: 3 => {{ string | round | json }} 4) Remove duplicate values from the array, expected answer: ["1","2","3"] => {{ array | plus: 0 | json }} 5) Halve the number 3, expected answer: 1.5 => {{ integer | downcase | json }} 6) Round the decimal number to the nearest whole number, expected answer: 1 => {{float | downcase | json }}