Using Liquid in Jekyll - live with demos

Using Liquid in Jekyll - live with demos

Liquid is an open-source template language created by Shopif that written in Ruby. Jekyll uses it to process templates and pages on your Jekyll site. With this powerful tool, you can get complex tasks done without additional plugins.

You can find Liquid documents at https://shopify.github.io/liquid/, and how Jekyll using it via https://jekyllrb.com/docs/templates/.

Here, I’m going to demonstrate examples from Liquid documents, how it works and test its ability in Jekyll. As this post was generated by Jekyll, the following examples should work correctly when you put in practise.

Liquid Basics

Liquid code can be categorized into objects, tags, and filters.

Objects

Objects tell Liquid where to show content on a page and denoted by double curly braces: {{ and }}.

Input:
{{ page.title }}

Output:
Using Liquid in Jekyll - live with demos

Tags

Tags create the logic and control flow for templates. They are denoted by curly braces and percent signs: {% and %}.

The markup used in tags does not produce any visible text. This means that you can assign variables and create conditions and loops without showing any of the Liquid logic on the page.

Input:
{% if statement %}
  Hello, you are visiting {{ page.url | prepend: site.url }}.
{% endif %}

Output:
  Hello, you are visiting https://flinhong.com/2016/08/20/using-liquid-in-jekyll/.

Tags can be categorized into three types (we’ll talk about them later in details):

  1. Control flow
  2. Iteration
  3. Variable assignments

Filters

Filters change the output of a Liquid object. They are used within an output and are separated by a |.

Input:
{{ page.url | append: '.html' }}

Output:
/2016/08/20/using-liquid-in-jekyll/.html

Operators

Basic Operators

== equals
!= does not equal
> greater than
< less than
>= greater than or equal to
<= less than or equal to
or logical or
and logical and

For example:

{% if product.title == "Awesome Shoes" %}
  These shoes are awesome!
{% endif %}

You can use multiple operators in a single tag:

{% if product.type == "Shirt" or product.type == "Shoes" %}
  This is a shirt or a pair of shoes.
{% endif %}

Contains

contains checks for the presence of a substring inside a string:

{% if page.title contains 'Pack' %}
  This page's title contains the word Pack.
{% endif %}

contains can also check for the presence of a string in an array of strings:

{% if page.tags contains 'Hello' %}
  This page has been tagged with 'Hello'.
{% endif %}

contains can only search strings. You cannot use it to check for an object in an array of objects.

Truthy and Falsy

Truthy

All values in Liquid are truthy except nil and false. In the example below, the string “Tobi” is not a boolean, but it is truthy in a conditional:

{% assign tobi = "Tobi" %}

{% if tobi %}
  This condition will always be true.
{% endif %}

strings, even when empty, are truthy.

Falsy

The falsy values in Liquid are nil and false.

Object Types

Liquid objects can have one of these types:

  • string
  • number
  • boolean
  • nil
  • array

You can initialize Liquid variables with the assign or capture tags.

String

Declare a string by wrapping a variable’s value in single or double quotes:

{% assign my_string = "Hello World! This is Frank Lin." %}

Number

Numbers include floats and integers:

{% assign my_int = 25 %}
{% assign my_float = 39.756 %}

Boolean

Booleans are either true or false. No quotations are necessary when declaring a boolean:

{% assign foo = true %}
{% assign bar = false %}

Nil

Nil is a special empty value that is returned when Liquid code has no results. It is not a string with the characters “nil”. Nil is treated as false in the conditions of if blocks and other Liquid tags that check the truthfulness of a statement.

In the following example, the page.author does not exist (i.e. {{ page.author }} returns nil), Liquid will not print anything:

{% if page.author %}
  post by {{ page.author }}
{% endif %}
{% if page.title %}
 the title of this post: {{ page.title }}
{% endif %}

Output:

 the title of this post: Using Liquid in Jekyll - live with demos

Array

Arrays hold lists of variables of any type. You cannot initialize arrays using only Liquid. You can, however, use the split filter to break a string into an array of substrings.

Accessing items in arrays:

Input:
{% for tag in page.tags %} {{ tag }} {% endfor %}

Output:
 Jekyll  Liquid 

Accessing specific items in arrays

You can use square bracket [ ] notation to access a specific item in an array. Array indexing starts at zero.

Input:
{{ page.tags[1] }}

Output:
Liquid

Whitespace Control

In Liquid, you can include a hyphen in your tag syntax {{-, -}}, {%-, and -%} to strip whitespace from the left or right side of a rendered tag. Normally, even if it doesn’t output text, any line of Liquid in your template will still output a blank line in the rendered HTML:

Input:
{% assign my_variable = "tomato" %}
{{ my_variable }}

Output:

tomato
# notice the blank line before “tomato” in the rendered result

By including hyphens in your assign tag, you can strip the generated whitespace from the rendered template. But it seems not work when building with Jekyll 3.2.1 locally on my Windows PC.

Input:
{%- assign my_variable = "tomato" -%}
{{ my_variable }}

Output:
# jekyll 3.2.1 | Error:  Liquid syntax error: Tag '{%- assign my_variable = "tomato" -%}' was not properly terminated with regexp: /\%\}/

Liquid Tags

Control Flow

Control flow tags can change the information Liquid shows using programming logic.

case / when

Creates a switch statement to compare a variable with different values. case initializes the switch statement, and when compares its values.

Input:
{% assign handle = 'cake' %}
{% case handle %}
  {% when 'cake' %}
     This is a cake
  {% when 'cookie' %}
     This is a cookie
  {% else %}
     This is not a cake nor a cookie
{% endcase %}

Output:
This is a cake
# whitespaces were cleaned intentionally

if

Executes a block of code only if a certain condition is true.

Input:
{% if page.category == 'Web' %}
  This post is in Web.
{% endif %}

Output:

unless

The opposite of if – executes a block of code only if a certain condition is not met. This would be the equivalent by using if ... !=:

Input:
{% unless page.category == 'Awesome' %}
  This post is not awesome.
{% endunless %}

Output:
  This post is not awesome.

elsif / else

Adds more conditions within an if or unless block.

{% if customer.name == 'kevin' %}
  Hey Kevin!
{% elsif customer.name == 'anonymous' %}
  Hey Anonymous!
{% else %}
  Hi Stranger!
{% endif %}

Iteration

Iteration tags run blocks of code repeatedly.

for

Repeatedly executes a block of code. For a full list of attributes available within a for loop, click to see the full doc.

Input:
{% for item in page.array %}
  {{ item }}
{% endfor %}
# page.array = ["1", "2","3","4","5","6","7"]

Output:
 1  2  3  4  5  6  7 

break

Causes the loop to stop iterating when it encounters the break tag.

Input:
{% for i in (1..10) %}
  {% if i == 4 %}
    {% break %}
  {% elsif i == 9 %}
    {% break %}
  {% else %}
    {{ i }}
  {% endif %}
{% endfor %}

Output:
 1  2  3 

for (parameters)

limit

Limits the loop to the specified number of iterations.

offset

Begins the loop at the specified index.

Input:
{% for item in page.array offset:2 limit:4 %}
  {{ item }}
{% endfor %}

Output:
 3  4  5  6 

range

Defines a range of numbers to loop through. The range can be defined by both literal and variable numbers.

Input:
{% assign num = 4 %}
{% for i in (1..num) %}
  {{ i }}
{% endfor %}

Output:
 1  2  3  4 

reversed

Reverses the order of the loop.

Input:
{% for item in page.array reversed %}
  {{ item }}
{% endfor %}

Output:
 7  6  5  4  3  2  1 

cycle

Loops through a group of strings and outputs them in the order that they were passed as parameters. Each time cycle is called, the next string that was passed as a parameter is output. cycle must be used within a for loop block.

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

Output:
one
two
three
one

Uses for cycle include:

  • applying odd/even classes to rows in a table
  • applying a unique class to the last product thumbnail in a row

cycle (parameters)

cycle accepts a parameter called cycle group in cases where you need multiple cycle blocks in one template. If no name is supplied for the cycle group, then it is assumed that multiple calls with the same parameters are one group.

tablerow

Generates an HTML table. Must be wrapped in opening <table> and closing </table> HTML tags.

<table>
{% tablerow item in page.array %}
  {{ item }}
{% endtablerow %}
</table>

Output:
<tr class="row1">
<td class="col1">
1
</td><td class="col2">
2
</td><td class="col3">
3
</td><td class="col4">
4
</td><td class="col5">
5
</td><td class="col6">
6
</td><td class="col7">
7
</td></tr>

tablerow (parameters)

  1. cols: defines how many columns the tables should have.
  2. limit: dxits the tablerow after a specific index.
  3. offset: starts the tablerow after a specific index.
  4. range: defines a range of numbers to loop through. The range can be defined by both literal and variable numbers.

Variable

assign

Creates a new variable. Wrap a variable in quotations " to save it as a string as previous examples.

capture

Captures the string inside of the opening and closing tags and assigns it to a variable. Variables created through {% capture %} are strings.

Input:
{% capture my_variable %}I am being captured.{% endcapture %}
{{ my_variable }}

Output:
I am being captured.

increment

Creates a new number variable, and increases its value by one every time it is called. The initial value is 0.

Input:
{% increment my_counter %}
{% increment my_counter %}
{% increment my_counter %}

Output:
0
1
2

Variables created through the increment tag are independent from variables created through assign or capture.

Input:
{% assign var = 100 %}
{{ var }}
{% increment var %}
{% increment var %}
{% increment var %}
{{ var }}

Output:
100
0
1
2
100

decrement

Creates a new number variable, and decreases its value by one every time it is called. The initial value is -1.

Similar to increment, variables declared inside decrement are independent from variables created through assign or capture.

Liquid Filters

String

append / prepend

append concatenates two strings and returns the concatenated value, and also can be used with variables.

prepend adds the specified string to the beginning of another string.

Input:
{% assign filename = "/index.html" %}
{{ site.url | append: filename }}

{{ "apples, oranges, and bananas" | prepend: "Some fruit: " }}

Output:
https://flinhong.com/index.html
Some fruit: apples, oranges, and bananas

capitalize

Makes the first character of a string capitalized, and only capitalizes the first character of the string, so later words are not affected:

Input:
{{ "this is a great title" | capitalize }}

Output:
This is a great title

cgi_escape

Escape a string for use in a URL.

Input:
{{ "foo,bar;baz?" | cgi_escape }}

Output:
foo%2Cbar%3Bbaz%3F

default

Allows you to specify a fallback in case a value doesn’t exist. default will show its value if the left side is nil, false, or empty.

Input:
{% assign product_price = "" %}
{{ product_price | default: 2.99 }}

Output:
2.99

downcase / upcase

Makes each character in a string lowercase (or upcase). It has no effect on strings which are already all lowercase (or upcase).

Input:
{{ "Frank Lin" | downcase }}
{{ "Frank Lin" | upcase }}

Output:
frank lin
FRANK LIN

escape / escape_once

escape works to escapes a string by replacing characters with escape sequences (so that the string can be used in a URL, for example). It doesn’t change strings that don’t have anything to escape.

escape_once can be used to escapes a string without changing existing escaped entities.

Input:
{{ "Have you read 'James & the Giant Peach'?" | escape }}
{{ "1 < 2 & 3" | escape_once }}

Output:
Have you read &#39;James &amp; the Giant Peach&#39;?
1 &lt; 2 &amp; 3

lstrip / rstrip

lstrip removes all whitespaces (tabs, spaces, and newlines) from the beginning of a string. The filter does not affect spaces between words. rstrip removes all whitespace from the right side of a string.

Input:
{{ "          So much room for activities!          " | lstrip }}
{{ "          So much room for activities!          " | rstrip }}

Output:
So much room for activities!          
          So much room for activities!

markdownify

Convert a Markdown-formatted string into HTML.

Input:
{{ "Hello **Jekyll**" | markdownify }}

Output:
<p>Hello <strong>Jekyll</strong></p>

newline_to_br

Replaces every newline (\n) with an HTML line break (<br>).

Input:
{% capture string_with_newlines %}
Hello
there
{% endcapture %}
{{ string_with_newlines | newline_to_br }}

Output:
Hello<br />
there<br />

number_of_words

Count the number of words in a string.

Input:
{{ "Hello Jekyll!" | number_of_words }}

Output:
2

remove / remove_first

Use remove to remove every occurrence of the specified substring from a string.

remove_first only removes the first occurrence of the specified substring from a string.

Input:
{{ "I strained to see the train through the rain" | remove: "rain" }}
{{ "I strained to see the train through the rain" | remove_first: "rain" }}

Output:
I sted to see the t through the 
I sted to see the train through the rain

replace / replace_first

Use replace to replace every occurrence of an argument in a string with the second argument.

And replace_first only replaces the first occurrence of the first argument in a string with the second argument.

Input:
{{ "Take my protein pills and put my helmet on" | replace: "my", "your" }}
{% assign my_string = "Take my protein pills and put my helmet on" %}
{{ my_string | replace_first: "my", "your" }}

Output:
Take your protein pills and put your helmet on
Take your protein pills and put my helmet on

size

Returns the number of characters in a string or the number of items in an array. size can also be used with dot notation (for example, {{ my_string.size }}). This allows you to use size inside tags such as conditionals.

Input:
{{ "Ground control to Major Tom." | size }}
{% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}
{{ my_array | size }}

{% if site.pages.size > 10 %}
  This site has more than 10 pages!
{% endif %}

Output:
28
4
This site has more than 10 pages!

slice

Returns a substring of 1 character beginning at the index specified by the argument passed in. An optional second argument specifies the length of the substring to be returned.

String indices are numbered starting from 0. If the first parameter is a negative number, the indices are counted from the end of the string:

Input:
{{ "Liquid" | slice: 0 }}
{{ "Liquid" | slice: 2 }}
{{ "Liquid" | slice: 2, 5 }}
{{ "Liquid" | slice: -3, 2 }}

Output:
L
q
quid
ui

slugify

Convert a string into a lowercase URL slug.

The slugify filter accepts an option:

  • none: no characters
  • raw: spaces
  • default: spaces and non-alphaunmeric characters
  • pretty: spaces and non-alphaunmeric characters except for ._~!$&'()+,;=@
Input:
{{ "The _config.yml file" | slugify }}
{{ "The _config.yml file" | slugify: 'pretty' }}

Output:
the-config-yml-file
the-_config.yml-file

smartify

Convert quotes into smart quotes with SmartyPants.

Input:
{{ 'Use "Jekyll" --- the static generator...' | smartify }}

Output:
Use “Jekyll” — the static generator…

split

Divides an input string into an array using the argument as a separator. split is commonly used to convert comma-separated items from a string to an array.

Input:
{{ "a~b" | split:"~" }}

Output:
ab

strip

Removes all whitespace (tabs, spaces, and newlines) from both the left and right side of a string. It does not affect spaces between words.

Input:
{{ "          So much room for activities!          " | strip }}

Output:
So much room for activities!

strip_html

Removes any HTML tags from a string.

strip_newlines

Removes any newline characters (line breaks) from a string.

truncate / truncatewords

truncate shortens a string down to the number of characters passed as a parameter. If the number of characters specified is less than the length of the string, an ellipsis (…) is appended to the string and is included in the character count.

Input:
{{ "Ground control to Major Tom." | truncate: 20 }}

Output:
Ground control to...

Custom Ellipsis

truncate takes an optional second parameter that specifies the sequence of characters to be appended to the truncated string. By default this is an ellipsis (…), but you can specify a different sequence. The length of the second parameter counts against the number of characters specified by the first parameter.

Input:
{{ "Ground control to Major Tom." | truncate: 25, ", and so on" }}

Output:
Ground control, and so on

truncatewords shortens a string down to the number of words passed as the argument. If the specified number of words is less than the number of words in the string, an ellipsis (…) is appended to the string. And Custom ellipsis can be also applied as truncate.

Input:
{{ "Ground control to Major Tom." | truncatewords: 3 }}

Output:
Ground control to...

uri_escape / xml_escape

Input:
{{ "foo, bar \baz?" | uri_escape }}
{{ "<p>Hi Jekyll</p>"| xml_escape }}

Output:
foo,%20bar%20%5Cbaz?
&lt;p&gt;Hi Jekyll&lt;/p&gt;

url_encode

Converts any URL-unsafe characters in a string into percent-encoded characters.

Input:
{{ "franklin@flinhong.com" | url_encode }}
{{ "Frank Lin" | url_encode }}

Output:
franklin%40flinhong.com
Frank+Lin

Integer

abs

Returns the absolute value of a number, and also work on a string if the string only contains a number.

Input:
{{ -17 | abs }}
{{ "-19.86" | abs }}

Output:
17
19.86

ceil

Rounds the input up to the nearest whole number. Liquid tries to convert the input to a number before the filter is applied.

Input:
{{ 183.357 | ceil }}
{{ "3.5" | ceil }}

Output:
184
4

divided_by / times

divided_by divides a number by the specified number. The result is rounded down to the nearest integer (that is, the floor) if the divisor is an integer.

divided_by produces a result of the same type as the divisor — that is, if you divide by an integer, the result will be an integer. If you divide by a float (a number with a decimal in it), the result will be a float.

times multiplies a number by another number.

Input:
{{ 16 | divided_by: 4 }}
{{ 5 | divided_by: 3 }}
{{ 20 | divided_by: 7.0 }}
{{ 3 | times: 2 }}
{{ 183.357 | times: 12 }}

Output:
4
1
2.857142857142857
6
2200.284

floor

Rounds a number down to the nearest whole number. Liquid tries to convert the input to a number before the filter is applied.

Input:
{{ 1.2 | floor }}
{{ "3.5" | floor }}
{{ 183.357 | floor }}

Output:
1
3
183

minus / plus

Subtracts (adds) a number from another number.

Input:
{{ 4 | minus: 2 }}
{{ 183.357 | minus: 12 }}
{{ 4 | plus: 1 }}

Output:
2
171.357
5

modulo

Returns the remainder of a division operation.

Input:
{{ 3 | modulo: 2 }}
{{ 183.357 | modulo: 12 }}

Output:
1
3.357

round

Rounds an input number to the nearest integer or, if a number is specified as an argument, to that number of decimal places.

Input:
{{ 1.2 | round }}
{{ 2.7 | round }}
{{ 183.357 | round: 2 }}

Output:
1
3
183.36

Array

array_to_sentence_string

Input:
{{ page.array | array_to_sentence_string }}

Output:
1, 2, 3, 4, 5, 6, and 7

first / last

Returns the first (or last) item of an array.

Input:
{% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}
{{ my_array.first }}
{{ my_array.last }}

Output:
apples
plums

group_by

Group an array’s items by a given property.

join

Combines the items in an array into a single string using the argument as a separator.

Input:
{% assign beatles = "John, Paul, George, Ringo" | split: ", " %}
{{ beatles | join: " and " }}

Output:
John and Paul and George and Ringo

jsonify

Convert Hash or Array to JSON.

Input:
{{ page.array | jsonify }}

Output:
["1","2","3","4","5","6","7"]

map (extract)

Creates an array of values by extracting the values of a named property from another object.

{% assign all_categories = site.posts | map: "category" %}
{% for item in all_categories limit:3 %}
{{ item }}
{% endfor %}

push

Adds an object to a array.

Creates an array of values by extracting the values of a named property from another object.

Input:
{% assign my_array = "a,b,c" | split:"," %}
{% assign my_array = my_array | push: 'd' %}
{{ my_array | array_to_sentence_string }}

Output:
a, b, c, and d

reverse

Reverses the order of the items in an array. Although reverse cannot reverse a string, but you can split a string into an array, reverse the array, and rejoin it by chaining together filters:

Input:
{% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}
{{ my_array | reverse | join: ", " }}

{{ "Ground control to Major Tom." | split: "" | reverse | join: "" }}
{{ "Ground control to Major Tom." | split: " " | reverse | join: " " }}

Output:
plums, peaches, oranges, apples

.moT rojaM ot lortnoc dnuorG
Tom. Major to control Ground

size

Return the size of an array or string.

sort

Sorts items in an array by a property of an item in the array. The order of the sorted array is case-sensitive.

Input:
{% assign my_array = "zebra, octopus, giraffe, Octopus" | split: ", " %}
{{ my_array | sort | join: ", " }}

Output:

Octopus, giraffe, octopus, zebra

uniq

Removes any duplicate elements in an array.

Input:
{% assign my_array = "ants, bugs, bees, bugs, ants" | split: ", " %}
{{ my_array | uniq | join: ", " }}

Output:
ants, bugs, bees

where

Select all the objects in an array where the key has the given value.

date

Converts a timestamp into another date format. The format for this syntax is the same as strftime. It also works on strings if they contain well-formatted dates:

Input:
{{ page.date | date: "%a, %b %d, %y" }}
{{ "August 20, 2016" | date: "%b %d, %y" }}

Output:
Sat, Aug 20, 16
Aug 20, 16
%a Abbreviated weekday Sun
%A Full weekday name Sunday
%b Abbreviated month name Jan
%B Full month name January
%c Preferred local date and time representation Fri Jan 29 11:16:09 2016
%d Day of the month, zero-padded 05
%-d Day of the month 5
%D Formats the date 29/01/16
%e Day of the month 3
%F Returns the date in ISO 8601 format 2016-01-29
%H Hour of the day, 24-hour clock 07
%I Hour of the day, 12-hour clock 07
%j Day of the year 017
%k Hour of the day, 24-hour clock 7
%m Month of the year 04
%M Minute of the hour 13
%p Meridian indicator uppercase AM
%P Meridian indicator lowercase pm
%r 12-hour time 01:31:43 PM
%R 24-hour time 18:09
%T 24-hour time with seconds 18:09:13
%s Number of seconds since 1970-01-01 00:00:00 UTC 1452355261
%S Second of the minute 05
%U Week number of the current year, starting with the first Sunday as the first day of the first week 03
%W Week number of the current year, starting with the first Monday as the first day of the first week 09
%w Day of the week. Sunday is 0 5
%x Preferred representation for the date 05/11/15
%X Preferred representation for the time 17:15:31
%y Year without a century 16
%Y Year with century 2016
%Z Time zone name (PST)  
%% Literal % character  

Besides, date_to_long_string convert a date to long format; date_to_long_rfc822 convert a Date into RFC-822 format; date_to_string convert a date to short format and date_to_xmlschema convert a Date into ISO 8601 format.

Input:
{{ page.date | date_to_long_string }}
{{ page.date | date_to_rfc822 }}
{{ page.date | date_to_string }}
{{ page.date | date_to_xmlschema }}

Output:
20 August 2016
Sat, 20 Aug 2016 00:00:00 +0800
20 Aug 2016
2016-08-20T00:00:00+08:00
avatar

Frank Lin

Code learning...

Say something Login