Getting started with Liquid

Updated on 20-October-2016 at 10:16 AM

Business Catalyst End of life announcement - find out more details.

Let's take a look at the basic Liquid features we can use. So when BusinessCatalyst has worked out what needs to be rendered, it parses the page (template, layout, etc.) and looks for Liquid placeholders. When these are encountered it replaces the Liquid code with the relevant data from your site.

Note: Through this article we have used the <pre>{{ this | json }}</pre> snippet. One of the most powerful things to have in your toolbox, the json filter will output the referenced object in JSON syntax. When using this we practically request every Liquid object available on that page.

This is Liquid

Not a code that we will go into detail that much but this is how a blog post list layout may possibly look using Liquid.

{% if postFeaturedImage == "" %}
    {% assign postFeaturedImage = "http://placehold.it/250x200" -%}
{% endif %}

<div class="row">
    <div class="col-md-4 col-sm-4">
    <a href="{{ url }}" title="Read more">
        <img alt="{{ title }}" class="img-responsive" src="{{ postFeaturedImage }}" >
    </a>
    </div>
    <div class="col-md-8 col-sm-8">
        <h2><a href="{{ url }}" title="{{ title }}">{{ title | capitalize }}</a></h2>
        <ul class="blog-info">
            <li><i class="fa fa-calendar"></i> {{ date | date: "dddd, MMMM yyyy" }}</li>
            <li><i class="fa fa-comments"></i> {{ commentsCount }}</li>
            <li><i class="fa fa-user"></i> {{ author }}</li>
        </ul>
        <p>{{ body | truncate: 350 }}</p>
        <a href="{{ url }}" class="more">Read more <i class="icon-angle-right"></i></a>
    </div>
</div>

Inside a template with custom styling it will output something like this:

As you can see, there's nothing too complicated, you'll soon get the hang of it.

Two types of placeholders

A placeholder is a piece of code that will ultimately be replaced when the page is sent to the browser.

The double parentheses {{ }} denotes output

Here's a quick example of an output placeholder for the name of a product in the Individual product (large) layout:

<h1>{{ name }}</h1>

When rendered, this would output the name of the currently viewed product in place of the {{ }}, for example:

<h1>Panda World T-Shirt</h1>

Output, unless manipulated with a filter (which we will look at later) is simply a case of replacing the placeholder with text from your site.

The parentheses percentage {% %} denotes logic

Let's take the example used above:

{% if postFeaturedImage == "" %}
    {% assign postFeaturedImage = "http://placehold.it/250x200" -%}
{% endif %}

We use two logic tags, if and assign and we want to achieve is clearly self-explanatory in our result. if there isn't a featured image for that specific post then we assign another string to it.

Tip: What's the difference between %} and -%}? Here's how to handle Liquid generated whitespace.

Globals accessible everywhere

Despite the fancy name, the Liquid global object consists of a set of variables that are always available on any page in BC. No matter if you are on a layout, custom layout, page, error screen and so on you will always have access to the global variables.

To see what the Liquid global object contains simply create a new blank page and insert this code snippet:

<pre>{{ globals | json }}</pre>

Note: {{ globals }} and {{ this.globals }} mean exactly the same thing. If the system does not detect "this" in front of your variable name, it assumes it is already there. Similarly, in a layout context for example {{ name }} and {{ this.name }} will output the same thing.

Open up the page in the front-end you will get something like this:

{
  "get": {
    "ID": "/test2.html"
  },
  "cookie": {
    "visitorDeviceClass": "desktop",
    "ASP.NET_SessionId": "x54fgiweu1zwhoisjetfsv5r",
    "ANONID_FS1576341": "24.09.2014 12:21:51.298",
    "ANONID1576341": "e626ac8b-45ff-4801-9808-ec9ad51d847a",
    "VISID1576341": "877790b5-3feb-499d-8227-a12cb6a5f388#mtica4.businesscatalyst.com#24.09.2014 12:21:51.298"
  },
  "site": {
    "id": 1576341,
    "name": "mtica4",
    "host": "mtica4.businesscatalyst.com",
    "countryCode": "US",
    "language": "US",
    "dateNow": "2015-01-09T12:05:00.7460201"

  },
 "user": {
    "entityId": 10524619,
    "firstname": "John",
    "lastname": "Doe",
    "fullname": "John Mark Doe",
    "username": "jdoe",
    "email": "john_doe@mailinator.com",
    "isLoggedIn": true
  },
  "visitor": {
    "deviceClass": "desktop",
    "ip": "193.2.2.2",
    "country": "RO",
    "userAgent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.103 Safari/537.36"
  },
  "cart": {
    "cartUrl": "/OrderRetrievev2.aspx?CatalogueID=0"
  }
}

Note: If you explore the global object on the same browser you are logged into the Admin console with you will notice alot more items in the cookie object. The extra cookies are created by the Admin console itself and will not be visible to site visitors. For a more detailed break-down of the global object take a look at the reference guide.

Understand JSON syntax

As you could have noticed in the examples used above, we've used the json filter to see the Liquid objects we have available in our page. This is useful as it renders the object specified in JSON format, a subset of the JavaScript syntax.

JSON has the following rules:

  • Data is in name/value pairs
  • Data is separated by commas
  • Curly braces hold objects
  • Square brackets hold arrays

JSON values can be:

  • A number (integer or floating point)
  • A string (in double quotes)
  • A Boolean (true or false)
  • An array (in square brackets)
  • An object (in curly braces)
  • null

Taking the {{ globals.cart | json }} as an example again, we can see from the curly braces that globals.cart is an object and we can notice the following data when looking at it's JSON representation:

  • itemCount has an integer value
  • totalAmount has a floating point value
  • countryCode, currencyFormat and cartUrl have strings as values
  • items is an array that has 2 objects
  • each object in the items array has additional data
  • the data is separated by commas

Output data from JSON

From previous examples it was easy to determine that when you want to output data from a Liquid object you need to enter it's name into {{ }}.

What about when you want to output data from a Liquid object that has another object inside of it and so forth?

Easy, you just need to concatenate the objects' name with a dot: {{ globals.cart.itemCount }} - will output 3 from our previous example.

How can we output items from an array?

If you would do {{ globals.cart.items.name }} it won't output anything (null) as Liquid does not know what name you are referring to, it expects an index. Indexes in JavaScript start from 0, so the first object inside an array would be 0, the second would have index 1 and so on.

Going further with this example, to get the name of the first product in the cart you would need to do {{ globals.cart.items[0].name }}.

In case you don't know the position or index of the object in the array then you can use a for loop to iterate through it.

Liquid data types

We created an entire article for Liquid data types but here is the gist of them:
  • strings - they can be declared inside placeholder by using single or double quotes
  • numbers - include floats and integers
  • boolean - either true or false, no quotes necessary when declaring
  • null - empty value returned (but not outputted) when Liquid code has no results
  • arrays - hold a list of variables of all types.

Filters - manipulate data

We mentioned them a few times now and that's because they are really powerful, from modifing simple strings to manipulating arrays or format date objects.

Filters are used in conjunction with an output tag. Their purpose it to manipulate the data in some way so that it's format changes. A great example is the date filter:

{{ globals.site.dateNow | date: "yyyy-%M-%d"}}

Outputs: 2015-12-7

Without the filter, BC would simply ouput the current timestamp. However by adding in the | and including the date filter, we can manipulate the date format ourselves.

We can also concatenate filters if we need to make additional changes to our data. Let's say we want to show our visitors how much they will save if we have a product on sale:

{{ salePrice | minus: retailPrice | times: 100 | divided_by: retailPrice | round | convert: "string" | replace: "-", "" }}

The filters will be applied from left ro right in this order:

  1. retailPrice (e.g 38) - salePrice (e.g 34.99) = temporary1 (-3.01)
  2. temporary1 * 100 = temporary2 (-301.00)
  3. temporary2 / retailPrice = temporary3 (-7.921...)
  4. round temporary3 = temporary4 (-8)
  5. convert temporary4 to string = temporary5 ("-8")
  6. remove the "-" from temporary5 = output ("8")

Each filter takes the output from its preceding filter and in turn modifies it. When there are no further filters to pass data into, the result is output as HTML into the page.

Note: There are many really useful filters, for a complete list please check the reference page of supported Liquid filters.

Logic Tags - control what to display

As well as allowing us to get data from our store and displaying it in our pages, as demonstrated above, Liquid is also able to control exactly what is displayed in our templates using logic constructs.

Example: We need to make sure the retail price and sale price used in the example above are not equal to 0 or that the product is on sale. You can follow this short article of how-to display percentage difference between the sale and retail prices for the complete implementation.

These tags are always wrapped in {% %} and represent the programming logic that tells Liquid what to do. Tags are broken into the following categories:

Variable Tags

These are used to create new Liquid variables.

assign

Let's take that example again:

{% assign postFeaturedImage = "http://placehold.it/250x200" -%}

We are assigning a new Liquid variable with the name postFeaturedImage with a string as the data. Always use quotations ("") to save the variable as a string.

Output of {{ postFeaturedImage }} would be: http://placehold.it/250x200

You can add boolean values to a variable by simply using true or false: {% assign onSale = true -%}

You can add numbers to a variable by not including quotations: {% assign myStock = 250 -%}

capture

This tag captures the string inside of the opening and closing tags and assigns it to a variable. The assigned variables are always strings.

{% capture myVariable %}This is a string.{% endcapture %}

Output of {{ myVariable }} would be: This is a string.

Theme Tags

These tags have various functions:

include

It inserts the code from the referenced file of the tag.

{% include 'path/to/my/file.extension' %}

When a snippet is included, the code inside it will have access to the variables within its parent page.

Note: See an include example from our reference pages.

comment

Comments out a block of text. The text between the comment tags does not render in the front-end.

{%comment%} Do not forget to remove comments {%endcomment%}

raw

This tag renders Liquid markup in plain text. This is very useful when using 3rd party API's or Javascript frameworks that use {{ }} aswell.

{% raw %}{{ myVariable }}{% endraw %} will output: {{ myVariable }}

Control Flow Tags

These tags determine which block of code should be executed based on different conditions.

Liquid has access to all of the logical and comparison operators, many of which you will find yourself using regularly:

  • == equal
  • != not equal
  • > greater than
  • < less than
  • >= bigger or equal
  • <= less or equal
  • or this or that
  • and must be this and that
  • contains includes the substring if used on a string, or element if used on an array

Note: For a short contains example please check our supported operators reference page.

if

We already used this in our first example, it executes the block of code inside it's tags if a certin condition is met.

{% if item.Color == 'red' -%} 
      <p>Red is the color</p> 
{% endif -%} 

elsif / else

It adds more conditions within an if tag.

{% if item.Color == 'red' -%}
   <p>Red is the color</p>
{% elsif item.Color == 'blue' -%}
  <p>Blue is the color</p>
{% else -%}
  <p>Neither red nor blue.</p>
{% endif -%}

unless

It's similar to if but it executes the block of code only if the certain condition is not met.

{% unless item.Color == 'red' -%}  
    <p>The color is not red.</p> 
{% endunless -%}

case

Do something based on the possible values of a variable. If none of the "known" values are encountered the last "else" branch will be executed.

{% case item.country -%}
   {% when 'DE' -%}
        Willkommen   
   {% when 'ES' -%}
        Bienvenido   
   {% when 'EN' -%}
        Welcome   
   {% else -%}     
        Bine ai venit 
{% endcase -%}

Iteration Tags

These tags are used to run a block of code repeatedly.

for

This deserves some attention as you'll be using it all the time. We are also able to use Liquid to output the same piece of code numerous times - for example a series of product images. If you have done any form of basic programming the concept of looping over data will be very familiar to you.

Using a loop, often known as a for loop, allows us to output the same piece of code a known number of times in our page. Let's have a look at an example where you would need to have products in cart but can be applied on any page:

 {% for item in globals.cart.items -%}
 	<p>{{ item.name }} <small>x{{ item.quantity }}</small></p>
 {% endfor -%}

We are using a loop to output the products' name and quantity of products in our visitors' cart. Let's break it down into steps to fully understand it.

Step 1: {% for item in globals.cart.items -%}

Our opening line introduces us to the idea of collections in Liquid. A Liquid collection in can take many forms and it is easy to spot as it normally takes the plural form - as in items above. In our example we are dealing with a Liquid collection of all the items in the cart from our globals object.

You'll also notice that we are using the word item to denote the current item in the loop. Each time we go round our loop, item will give us access to the data associated with each product in turn. Naturally this will be different on each loop.

Note: We don't need to know how many loops will occur. After there are no more images to loop over BC will carry on and render the next part of the page.

Liquid is really powerful and whilst you might think we are getting into programming territory, I am confident you'll pick it up pretty quickly.

Step 2:<p>{{ item.name }} <small>x{{ item.quantity }}</small></p>

The second line of our code is part HTML and part Liquid. This short construct will populate the p tag with our products' name and our products' quantity within the small tag.

Step 3: {% endfor -%}

The final line of our example is our closing endfor statement. This effectively closes off any code that will be rendered within the loop.

If we had two products in our cart and our {{ globals.cart | json }} object would look like this:

{
  "itemCount": 3,
  "totalAmount": 80.97,
  "countryCode": "US",
  "currencyFormat": "$",
  "items": [
    {
      "id": 407650,
      "productId": 9691399,
      "catalogId": 314592,
      "name": "Cool Dress",
      "code": "ASFGWQE11",
      "description": "XS",
      "price": 34.990000,
      "quantity": 2,
      "totalPrice": 69.980000,
      "smallImage": "/assets/frontend/pages/img/products/p1_sq.jpg",
      "url": "/dresses/cool-green-dress-with-red-bell"
    },
    {
      "id": 407936,
      "productId": 9683563,
      "catalogId": 176855,
      "name": "Panda World T-Shirt",
      "code": "SHDA9ZVVWY",
      "description": "M;green",
      "price": 10.990000,
      "quantity": 1,
      "totalPrice": 10.990000,
      "smallImage": "/shop/tshirt2.jpg",
      "url": "/all-t-shirts/panda-world-t-shirt"
    }
  ],
  "cartUrl": "/OrderRetrievev2.aspx?CatalogueID=0"
}

The final HTML output would look like this:

<p>Cool Dress <small>x2</small></p>
<p>Panda World T-Shirt <small>x1</small></p>

Loops are really useful and something you will encounter daily in your BC development.

Warning: When editing Liquid enabled layouts or pages only use the Develop tab editor as the WYSIWYG editor will break the page formatting.

cycle

This tag loops through a group of strings and outputs them one by one, in the order they were found in that group. Each time cycile is called, the next string is outputted.

{% cycle 'red', 'green', 'blue' -%}  
{% cycle 'red', 'green', 'blue' -%}  
{% cycle 'red', 'green', 'blue' -%}  
{% cycle 'red', 'green', 'blue' -%}

Will output: red green blue red

tablerow

This will draw a table, but the opening and closing table tags need to be present. You need to specify how many columns the table will contain using the cols: parameter.

<table>
 {% tablerow item in webapp1.items cols:2 -%}
      {{item.name}} - {{item.counter}}
 {% endtablerow -%}
</table>

Note: Check out the reference page of Liquid logic tags for more.

Extending BC with Liquid

This is not all, we have another article for you with a list of BusinessCatalyst features that use Liquid to help you in your projects.