Skip to content

Built-in tags

Comments

Syntax
{# <comment text> #}

Comments can be used to add documentation to your templates or "comment out" chunks of markup so that it wont be rendered.

{# This is a comment #}
{#
    Comments can
    span
    multiple lines
#}

You can safely comment out markup containing comments by adding hashes, as long as the number of hashes is balanced.

{##
  {# existing comment #}
  {{ something }}
##}

By convention, comments that start with a single hash on every line are considered doc comments to be parsed by a documentation generator.

{##
 # @param font
 # @returns CSS
 ##}

Output

Syntax
{{ <expression> }}

An expression surrounded by double curly braces ({{ and }}) is an output statement. When rendered, the expression will be evaluated and the result written to the output stream.

In this example the expression is the variable you - which will be resolved to a value and the value's string representation will be written to the output stream - but output statements can contain any expression.

Hello, {{ you }}!

assign

Syntax
{% assign <identifier> = <expression> [, <identifier> = <expression> ...] %}

The assign tag binds the result of evaluating an expression to a name, creating or updating a variable.

{% assign foo = "bar" %}
{% assign foo = 42 %}

A single assign tag can define multiple variables, separated by commas. Earlier bindings are immediately available in later expressions.

{% assign
  a = 1,
  b = a + 1
%}

capture

Syntax
{% capture <identifier> %} <markup> {% endcapture %}

The capture tag renders a block of markup to a string variable instead of writing it directly to the output stream.

{% assign first_name = "Sam" %}
{% assign points = 120 %}

{% capture message %}
Hello {{ first_name }}!
You have {{ points }} reward points.
{% endcapture %}

<p>{{ message }}</p>
Output
<p>Hello Sam!
You have 120 reward points.</p>

See also the define tag.

case

Syntax
{% case <expression> %}
  [ {% when <expression | predicate> [, <expression | predicate>] %} <markup> ] ...
  [ {% else %} <markup> ]
{% endcase %}

The case tag provides a multi-way conditional. It compares the result of a single expression against the result of one or more candidate expressions, and renders the block following the first matching {% when %} tag. If no branch matches, the optional {% else %} branch is rendered.

{% assign day = "Monday" %}

{% case day %}
  {% when "Monday" %}
    Start of the work week!
  {% when "Friday" %}
    It's almost the weekend!
  {% when "Saturday" or "Sunday" %}
    Enjoy your weekend!
  {% else %}
    Just another weekday.
{% endcase %}

when tags also understand bare predicates, like .defined? and .empty? in place of a standard expression.

{% case items %}
  {% when empty? %}
    No items.
  {% else %}
    {{ items | size }} items.
{% endcase %}

define

Syntax
{% define <identifier> [ visibility: ("public" | "private") ] %} <markup> {% enddefine %}

The define tag captures a block of markup for later rendering. define captures nothing about the scope in which it rendered. Defined blocks are rendered in the scope in which they are output or coerced to a string.

{% assign name = "Alice" %}

{% define greeting %}
Hello {{ name }}!
{% enddefine %}

{% assign name = "Bob" %}

{{ greeting }}
{{ greeting | upcase }}
Output
Hello Bob!
HELLO BOB!

define can be though of as a deferred capture. See also import.

for

Syntax
{% for <identifier> [, identifier [, identifier]] in <expression> %}
  <markup>
  [ {% else %} <markup> ]
{% endfor %}

The for tag renders its block once for each item in an iterable object. If the iterable is empty and an else block given, it will be rendered instead.

{% for product in collection %}
  - {{ product.title }}
{% else %}
  No products available
{% endfor %}

Optionally, the current zero-based iteration index can be bound to a name.

{% for product, index in collection %}
  - {{ product.title }} at index {{ index }}
{% endfor %}

And the optional third identifier binds the array being iterated to a name.

{% for product, index, array in collection %}
  {%- assign last = index == (array | size) - 1 -%}
  ...
{% endfor %}

break

You can exit a loop early using the break tag.

{% for product in collection.products %}
  {% if product.title == "Shirt" %}
    {% break %}
  {% endif %}

  - {{ product.title }}
{% endfor %}

continue

You can skip all or part of a loop iteration with the continue tag.

{% for product in collection.products %}
  {% if product.title == "Shirt" %}
    {% continue %}
  {% endif %}

  - {{ product.title }}
{% endfor %}

if

{% if <expression> %}
  <markup>
  [ {% elsif <expression> %} <markup> [ {% elsif <expression> %} ... ]]
  [ {% else %} <markup> ... ]
{% endif %}

The if tag conditionally renders its block if its expression evaluates to be truthy. Any number of elsif blocks can be given to add alternative conditions, and an else block is used as a default if no preceding conditions were truthy.

{% if product.title == "OK Hat" %}
  This hat is OK.
{% elsif product.title == "Rubbish Tie" %}
  This tie is rubbish.
{% else %}
  Not sure what this is.
{% endif %}

import

Syntax
{% import <string> [ as <identifier>] %}

The import tag loads and renders a named template for its side effects, discarding its output. import is used to bring variables (including blocks defined with the define tag) into scope.

{% import button_utils as buttons %}

include

Syntax
{% include <template name>
    [[,] <identifier>: <expression> [, [<identifier>: <expression> ... ]]]
%}

The include tag loads and renders a named template, inserting the resulting text in its place. The name of the template to include can be a string literal or a variable resolving to a string. When rendered, the included template will share the same scope as the "calling" template.

{% include "snippets/header.html" %}

Keyword arguments

Additional keyword arguments given to the include tag will be added to the included template's scope, then go out of scope after the included template has been rendered.

{% include "partial_template" greeting: "Hello", num: 3, skip: 2 %}

raw

{% raw %} <text> {% endraw %}

Any text between {% raw %} and {% endraw %} will not be interpreted as markup, but output as plain text instead.

{% raw %}
  This will be rendered {{verbatim}}, with the curly brackets.
{% endraw %}

render

Syntax
{% render <string>
    [[,] <identifier>: <expression> [, [<identifier>: <expression> ... ]]]
%}

The render tag loads and renders a named template, inserting the resulting text in its place. The name of the template to include must be a string literal. When rendered, the included template will have its onw scope, without variables define in the calling template.

{% render "snippets/header.html" %}

Keyword arguments

Additional keyword arguments given to the render tag will be added to the rendered template's scope, then go out of scope after the it has been rendered.

{% render "partial_template" greeting: "Hello", num: 3, skip: 2 %}

with

Syntax
{% with <identifier> = <expression> [, <identifier> = <expression> ... ] %}
  <markup>
{% endwith %}

The with tag introduces block-scoped variables. These variables have the potential to shadow global variables or variables assigned with {% assign %}, {% capture %} and {% define %}.

{% with p = collection.products.first %}
  {{ p.title }}
{% endwith %}

{% with a = 1, b = 3.4 %}
  {{ a }} + {{ b }} = {{ a + b }}
{% endwith %}

When multiple bindings are given, earlier variables are in scope for later expressions.

{% with
  a = 1,
  b = 3.4,
  c = a + b,
%}
  {{ a }} + {{ b }} = {{ c }}
{% endwith %}