Transition to Jinja templates from Django templates

If you're accustomed to using Django templates, this section describes the finer details you need to be aware of when using Jinja templates, such as: what Django template knowledge you can leverage in Jinja templates, what works differently in Jinja templates compared to Django templates and what are new things you need to learn that you'll come to appreciate in Jinja templates.

If you've never used Django templates, you can skip to the next section on Jinja template configuration in Django, as most of what follows is intended for experienced Django template users.

What works the same way in Jinja and Django templates

Just because Jinja is an entirely different template engine, doesn't mean it's radically different from Django's built-in template engine. You can expect to use the same approach for: Variables & blocks, conditionals & loops, comments, as well as spacing & special characters.

Variables and blocks

Curly braces {} are broadly used in Jinja templates just like they're used in Django templates. To output a variable in Jinja you use the same {{myvariable}} syntax. Similarly, you also name blocks to inherit snippets between templates with the {% block footer %} {% endblock %} syntax. In addition, Jinja also uses the same Django {% extends "base.html" %} syntax to create parent/child relationships between templates.

Conditionals and loops

Jinja uses the same Django syntax to create conditionals: {% if variable %}{% elif othervariable %}{% else %}{% endif %}. In addition, Jinja also uses the same for loop syntax as Django: {% for item in listofitems %}{{item}}{% endfor %}.


Jinja also uses the same comment tag as Django: {# This is a template comment that isn't rendered #}. However, note Jinja uses the {# #} tag for both single and multi-line comments.

Spacing and special characters

Since Jinja templates were inspired from Django templates, Jinja uses a similar approach to dealing with spacing and special characters. For example, things like spacing filters ( and wordwrap) and special character handling ( and escape filters ) work the same way in Jinja templates as they do in Django templates.

What works differently in Jinja templates compared to Django templates

However, not everything works the same way in Jinja templates, here are some Django template techniques you'll need to relearn to work with Jinja templates.


Although Jinja uses the same pipe | symbol to apply filters to variables, Jinja filters are technically classified into filters and tests. In Django templates there are just filters that perform tests (e.g. divisibleby) but in Jinja these type constructs are called tests and use the conditional syntax {% if variable is test %} instead of the standard pipe | symbol.

In addition, Jinja filters and tests are backed by standard methods. This has the advantage that passing arguments to Jinja filters and tests is as simple as a method call (e.g.{{variable|filesizeformat(true)}}) vs. the unintuitive Django filter argument syntax of using a colon and even requiring to parse arguments in custom Django filters (e.g. {{variable|get_digit:"1"}}).

It's also possible to create custom Jinja filters and tests -- in addition to the built-in Jinja filters and tests which are similar to Django built-in filters. However, unlike Django filters which are loaded into templates via the {% load %} tag, Jinja custom filters and tests are registered globally and become accessible to all Jinja templates like Django context processors.

Context processors

Context processors give Django templates access to sets of variables across every template in a project, but in Jinja this functionality is called global variables. This is one area where you'll likely miss the Django template functionality of simply declaring context processors and getting access to sets of variables. However, it's relatively easy to create Jinja global variables to become accessible on all Jinja templates and act as Django context processors.

No date elements like the {% now %} tag and filters like time and timesince

Jinja in its out-of-the-box state provides no tags or filters to work with dates or times. Although Jinja does offer the format filter that works just like Python's standard method and can be used for date formatting, you'll need to write your own custom filters and tags to deal with date and time elements in a more advanced way.

{% comment %} tag not supported

Jinja uses the {# #} tag to define either single and multi-line comments, so there's no support for the {% comment %} which in Django templates is used for multi-line comments.

{% load %} tag not supported

In Jinja the {% load %} tag to import custom tags and filters is not supported. In Jinja custom tags and filters are registered globally and automatically become accessible to all Jinja templates.

Use {{super()}} instead of {{block.super}}

In Django templates you use the syntax {{ block.super }} to access the contents of a parent template's block. In Jinja you must use the {{super()}} syntax to gain access to the contents of a parent template's block.

{% csrf_token %} tag not supported instead use csrf_input or csrf_token variables

In Django templates when you create a form that has an HTTP POST action, you place the {% csrf_token %} tag in its body to generate a special token that avoids XSS('Cross-site scripting'). To replicate this behavior in Jinja you must use the csrf_input variable (e.g. {{csrf_input}} generates a string like <input type="hidden" name="csrfmiddlewaretoken" value="4565465747487">) or use the csrf_token variable which contains the raw CSRF token (e.g.4565465747487).

{% for %} loop variables

In Django templates the context of {% for %} loops offers access to a series of variables (e.g. counter, first and last iteration). Jinja templates offer a similar variable in the context of {% for %} but they are not identical.

{% empty %} tag not supported in loops, use the {% else %} tag

{% for %} loops in Django templates support the {% empty %} clause as a last argument to generate logic or a message when an iteration is empty. In Jinja {% for %} loops you can use the {% else %} clause as a last argument to generate logic or a message when an iteration is empty.

{% groupby %} tag not supported, use the groupby filter

Django templates support the {% groupby %} tag to rearrange dictionaries or objects based on different attributes. In Jinja you can achieve the same functionality but you must do it through the groupby filter as described in the Jinja groupby filter.

{% cycle %} tag not supported, use the cycler function or the loop.cycle variable in {% for %} loops

Django templates support the {% cycle %} tag to cycle over a list of values. In Jinja this functionality is available in two forms. You can use the cycler method if you require the functionality outside of loops. Or you can use the loop.cycle function available in all {% for %} loops.

{% lorem %} tag not supported, use the lipsum function

Django templates support the {% lorem %} tag to generate random latin text as filler content. In Jinja you can achieve the same functionality with the lipsum function.

Other miscellaneous tags like {% static %}, {% trans %}, {% blocktrans %} and {% url %} not supported

A series of Django template tags like {% static %} and {% trans %} are simply not available in Jinja. However, there are third party projects that have ported these and many other Django template tags into Jinja extensions. A later section in this chapter on Jinja extensions discusses these options.

New concepts and features in Jinja templates vs. Django templates

Now that you know what Django template knowledge you can leverage and what techniques you'll need to relearn to effectively work with Jinja templates, let's take a look at some concepts that only apply to Jinja templates.

More useful built-in filters, tests and more resemblance to a Python environment

Jinja templates offer a variety of built-in filters and tests that are sorely missing in Django templates. For example, for something as simple as checking variable types (e.g. string, number, iterable, etc), Jinja offers a series of built-in tests for this purpose, where as in Django this requires creating custom filters.

Access and manipulation of complex data types (e.g. objects and dictionaries) is also vastly improved in Jinja templates vs. Django templates. For example, Jinja offers filters such as reject, select and map to prune, filter or alter data sub-sets on a template, a technique that although frowned upon by purists (i.e. those who stand by only manipulating data in views) are a very common requirement in real & time-constrained projects.

Jinja templates also support syntax that is more in-line with a standard Python environment. For example, in Django something like accessing a dictionary key through a variable requires a custom filter, where as in Jinja templates this works with standard Python syntax (e.g. if you have the variables stores={"key1":"value1", "key2":"value2"} and var="key1", Django template can't do stores.get(var) which is standard Python syntax, but in Jinja this works out-of-the-box as expected of a Python environment).

Global functions

Jinja also supports a series of global functions. For example, Jinja offers the range function that works just like Python's standard function which is useful in loops (e.g. {% for number in range(50 - coffeeshops|count) %}). In addition, Jinja also offers the global functions: lipsum to generate dummy placeholder content, dict to generate dictionaries, cycler to generate a cycle over elements and joiner to join sections.

Flexible tag nesting, conditionals and references

Jinja is very flexible in terms of nesting tags, particularly compared to what's permissible in Django templates. For example, in Jinja you can even conditionally apply the {% extends %} tag (e.g. {% if user %}{% extends "base.html" %}{% else %}{% extends "signup_base.html" %}{% endif %}) or also use variable reference names with inline conditions (e.g.{% extends layout_template if layout_template is defined else 'master.html' %}) something that's not possible in Django templates.


In Jinja macros allow you to define function-like snippets with complex layouts that can be called from any template with different instance values. Macros are particularly useful to limit the spread of complex layouts across templates. With macros you define a complex layout once (i.e. as a macro) and invoke it with different parameters to output the complex layout customized every single time, just as if were a function.

Flexible variable assignment in templates with less restrictive scope

In Jinja you can use the {% set %} tag to define variables to have a valid scope until the end of the template. Although Jinja also supports the {% with %} tag -- just like the Django template version - the {% with %} tag can become cumbersome for multiple variable definitions because it requires closing the scope with {% endwith %} every time. The {% set %} is a good alternative for global template variables because you only require the initial definition and the scope propagates to end of the template without having to worry about closing the scope.

Line statements

Jinja supports the definition of logical statements in what it calls line statements. By default, a line statement is preceded with the # symbol and can serve as an alternative to tag syntax. For example, the {% for %} tag statement {% for item in items %} can use the equivalent line statement # for item in items, just as the tag statement {% endfor %} can use the equivalent line statement # endfor . Line statements more than anything give templates a Python feel to them which can make complex logic easier to decipher vs. using tag statements that require the {% %} syntax.