Skip to main content

Jinja Syntax


Print statements include expressions, which get replaced with values when a template is rendered. As a simple expression, you can use a variable username so that your response includes the name of the user:

Good day to you, {{ username }}!

If the user name is Norman, then the response that is displayed to user is

Good day to you, Norman!

See also: Expressions.

{% ... %}

Template tags include statements that control template logic, evaluate expressions, assign variables and more.

See also: Statements.

{\# ... #}

Comments that are not included in the template output. This is useful to comment out parts of the template for debugging or to add information for other template designers or yourself:

{# note: commented-out template because we no longer use this
{% for user in users %}
{% endfor %}


This section contains only literal for None Type. There are also links to and examples of literals for basic data types:


The none literal is used to define a value of None Type.

See also: Data types.


This section contains only general purpose opeators. More type-specific operators are also availabe:

x.y / x[y]

Get an attribute of an object. You can use both a dot x.y and a "subscript" x[y] operators to access attributes of a variable. The following lines do the same thing:

{{ }}
{{ entities["menu"] }}


Call a function or method.

{{ title.capitalize() }}

Inside of the parentheses you can use positional arguments and keyword arguments like in Python:

{% set ns = namespace(found=true) %}

Typically, functions are available as global variables.

Methods are available for the following basic types:

x | y

Filters are a convenient way to change variables. Filters are separated from the variable by a pipe symbol x | y.

{{ x|upper }}

Filters may have optional arguments in parentheses.

{{ x|replace("Hello", "Goodbye") }}
{{ x|indent(width=8) }}

Multiple filters can be chained. The output of one filter is applied to the next.

{{ x|select|first }}

Filters are available for the following basic types:

x if y else z

A conditional expression, yields the value of x if y if true, otherwise the value of z if returned.

{% set n = numbers|first if numbers|length > 0 else -1 %}

See also: if-statement.


{% delete ... %}

Delete a state variable.

{% delete slots.counter %}
{% delete %}

The variable is considered deleted and no longer uses the bot's memory. Trying to get the value of a variable will result in an error.

See also: State Variables.

{% do ... %}

The expression-statement simply evaluates the expression and does nothing else. It does not print or assign the result.

This can be used to modify lists or dicts:

{% do toppings_array.append("ketchup") %}
{% do car.update(color="White") %}

See also: list.append, dict.update.

{% filter x %} ...

Filter block allows you to apply string filters on a block of template data. Just wrap the code in the special filter section:

{% filter upper %}
This text becomes uppercase
{% endfilter %}

Filters that accept arguments can be called like this:

{% filter indent(width=8) %}
Indent this
{% endfilter %}

See also: str|upper, str|indent.

{% for x in ... %}

You use the for-in loop to iterate over lists, dictionaries, strings, and other sequences.

Use for-statement to build a response text based on dynamically collected data. For example, to display the contents of the shopping cart to the user:

{% set products = [
{"title": "Anti-Gravity Boots"},
{"title": "Fountain of Youth"},
{"title": "Inflatable Flower Bed"},
] %}

Items in your bag.
{% for product in products %}
{{ product.title }}
{% endfor %}
Items in your bag.
Anti-Gravity Boots
Fountain of Youth
Inflatable Flower Bed

It is possible to iterate over dictionaries using method dict.items(). Dictionaries may not be in the order you want to display them in. If order matters, use the dict|dictsort filter.

Please note that assignments in loops will be cleared at the end of the iteration and cannot outlive the loop scope. See Scoping Behavior for more information about how to deal with this.

See also: Iterating over, dict.items() , dict|dictsort, Scoping Behavior.

{% for ... if ... %}

Filter the sequence during iteration, which allows you to skip items. The following example skips all the products which are not in stock.

Product List
{% set products = [{"id":1,"title":"olive", "in_stock":true}, {"id":2,"title":"ham"}, {"id":3,"title":"cheese", "in_stock":true}] %}
{% for product in products if product.in_stock %}
{{ product.title }} <br/>
{% endfor %}

See also: If-expression: x if y else z, {% if ... %}, {% for x in ... %}.

{% for ... %} - {% else %}

If no iteration took place because the sequence was empty or the filtering removed all the items from the sequence, you can render a default block by using else:

Product List
{% set products = [{"id":1,"title":"olive", "in_stock":true}, {"id":2,"title":"ham"}, {"id":3,"title":"cheese", "in_stock":true}] %}
{% for product in products %}
{{ product.title }} <br/>
{% else %}
no products found
{% endfor %}

See also: {% for x in ... %}, Scoping Behavior.

{% for ... recursive %}

It is possible to use loops recursively. This is useful if you are dealing with recursive data such as article references. To use loops recursively, you basically have to add the recursive modifier to the loop definition and call the loop variable with the new iterable where you want to recurse.

The following example shows article references with recursive loops:

{% set articles =[
"id": 1,
"title": "Title 1",
"references": [
"id": 5,
"title": "Title 5",
"references": [
"id": 6,
"title": "Title 6",
"references": []
"id": 7,
"title": "Title 7",
"references": []
"id": 2,
"title": "Title 2",
"references": []
"id": 3,
"title": "Title 3",
"references": []
{% for article in articles recursive %}
{{ article.title }};
{% if article.references %}
{{ loop(article.references)|indent(width=8) }}
{% endif %}
{% endfor %}
Title 1; Title 5; Title 6;
Title 7;
Title 2; Title 3;

See also: {% for x in ... %}, loop variable, str|indent.

{% if ... %}

Use if-statement to change the response based on a specific conditions. In the simplest form, you can use it to test if a variable is defined, not empty and not false.

If the product description is not defined or is an empty string, the description line should not be printed at all. Then the response may be the following.

Product Name: {{ }}.
{% if slots.product.description %}
Description: {{ slots.product.description }}
{% endif %}

Use elif- and else-statements for multiple branches. You can use more complex expressions there, too.

{%   if slots.total_points / slots.total_voters > 4 %}
Great stuff!
{% elif slots.total_points / slots.total_voters > 3 %}
A good thing.
{% else %}
Bad reviews...
{% endif %}

If can also be used as an inline If-expression: x if y else z and for loop filtering {% for ... if ... %}.

{% set x = ... %}

Assign value to a local variable.

{% set rating = slots.total_points / slots.total_voters %}

Assign value to slot variable.

{% set slots.counter = 1 %}

Assign value to user variable.

{% set = "Steve" %}

Assignments can have multiple targets separated by commas. In the example, variable x is set to 0 and variable y is set to 1.

{% set x, y = 0, 1 %}

See also: State Variables.

{% set x %} ...

Block assignments are used to capture the contents of a block into a local variable. Instead of using an equals sign and a value, you just write the variable name and then everything until {% endset %} is captured.

{% set reply %}
You wrote:
{{ message.text }}
{% endset %}

The block assignment supports filters.

{% set reply | truncate %}
You wrote:
{{ message.text }}
{% endset %}

See also: Filters, str|truncate.

{% set slots.x = ... %}

Assign value to slot variable.

{% set slots.counter = 1 %}

See also: Slot Variables, Working with State Variables.

{% set user.x = ... %}

Assign value to user variable.

{% set = "Steve" %}

See also: User Variables, Working with State Variables.


This section contains only general purpose filters. There also type-specific dict and range.


Creates a new container that allows attribute assignment using the {% set x = ... %} tag:

{% set ns = namespace() %}
{% set = "bar" %}

See also: Assignment, Scoping Behavior


This section contains only general purpose filters. More type-specific filters are also availabe:


If the value is undefined it will return the passed default value, otherwise the value of the variable:

{{ entities.order_number.literal|default("unknown order number") }}

This will output the value of entities.order_number.literal if the variable was defined, otherwise "unknown order number".

{% set slots.guests = entities.number.value|default(2) %}

If the entities.number is undefined, then the value of state variable slots.guests will be 2.

If you want to use default with variables that evaluate to false you have to set the second parameter to true:

{{ ''|default('the string was empty', true) }}

Aliases: x|d.

See also: Filters, Undefined Value.


This section contains only general purpose tests. More type-specific tests are also availabe:

x is defined

Return true if the variable or its attribute is defined.

{% if variable is defined %}
value of variable: {{ variable }}
{% else %}
variable is not defined
{% endif %}

See also: Tests, Undefined Value.

x is filter

Check if a filter exists by name. Useful if a filter may be optionally available.

{% if "markdown" is filter %}
{{ value | markdown }}
{% else %}
{{ value }}
{% endif %}

See also: Tests, Filters.

x is none

Return true if the variable is none.

See also: Tests, None Type.

x is test

Check if a test exists by name. Useful if a test may be optionally available.

{% if "loud" is test %}
{% if value is loud %}
{{ value|upper }}
{% else %}
{{ value|lower }}
{% endif %}
{% else %}
{{ value }}
{% endif %}

See also: Tests, str|upper, str|lower.

x is undefined

Return true if the variable or its attribute is undefined.

See also: Tests, Undefined Value.

loop variable

Inside of a for-statement block, you can access special loop variable.

The loop variable always refers to the closest (innermost) loop. If we have more than one level of loops, we can rebind the variable loop by writing {% set outer_loop = loop %} after the loop that we want to use recursively. Then, we can call it using outer_loop.index and so on.

loop.index / index0

  • loop.index - The current iteration of the loop. (1 indexed).
  • loop.index0 - The current iteration of the loop. (0 indexed).

loop.revindex / revindex0

  • loop.revindex - The number of iterations from the end of the loop (1 indexed).
  • loop.revindex0 - The number of iterations from the end of the loop (0 indexed).

loop.first / last

  • loop.first - True if first iteration.
  • loop.last - True if last iteration.


The number of items in the sequence.


A helper function to cycle between a list of sequences.

Within a for-loop, it’s possible to cycle among a list of strings/variables each time through the loop by using the special loop.cycle helper:

{% for row in rows %}
{{ loop.cycle('odd', 'even') }} {{ row }}
{% endfor %}

loop.depth / depth0

  • loop.depth - Indicates how deep in a recursive loop the rendering currently is. Starts at level 1.
  • loop.depth0 - Indicates how deep in a recursive loop the rendering currently is. Starts at level 0.

See also: {% for ... recursive %}.

loop.previtem / nextitem

  • loop.previtem - The item from the previous iteration of the loop. undefined during the first iteration.
  • loop.nextitem - The item from the following iteration of the loop. undefined during the last iteration.

If all you want to do is check whether some value has changed since the last iteration or will change in the next iteration, you can use previtem and nextitem:

{% for value in values %}
{% if loop.previtem is defined and value > loop.previtem %}
The value just increased!
{% endif %}
{{ value }}
{% if loop.nextitem is defined and loop.nextitem > value %}
The value will increase even more!
{% endif %}
{% endfor %}


True if previously called with a different value (or not called at all).

If you only care whether the value changed at all, using changed is even easier:

{% for entry in entries %}
{% if loop.changed(entry.category) %}
{{ entry.category }}
{% endif %}
{{ entry.message }}
{% endfor %}

JSON Compatibility

nullNone Type