Transition to Jinja templates in Django projects


You want to switch to Jinja templates in your Django projects and you're only familiar with Django templates. You want to know 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.

Note Jinja advantages, disadvantages and set up.

If you're still looking for Jinja template advantages and disadvantages or a Django-Jinja set up guide, see the previous the recipe Use and customize Jinja templates in Django.


Jinja and Django templates use the same variable, block, comment and spacing syntax. Jinja templates differ from Django templates in filter definitions, context processors, date elements and miscellaneous tag syntax. Jinja templates offer more useful built-in filters/tests, more resemblance to a Python environment, as well as global functions, flexible tag nesting and conditionals, macros, flexible variable assignment and line statements.

How it works

When switching to Jinja templates from Django templates, like most transitions, some things work exactly the same, some things require a completely different approach and some things are completely new. Next, I'll enlist the main points related to Jinja templates with respect to Django template in these three categories.

What works the same way in Jinja templates and Django templates

Variables and blocks

Curly braces {} which are broadly used in Django templates are also used extensively in Jinja templates. To output a variable in Jinja you use the same {{myvariable}} syntax. Similarly, you can 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

In Jinja you can also use 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 #}. Although note Jinja uses the {# #} tag for both single and multi-line comments.

Spacing and special characters

Since Jinja evolved in part 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 practically the same way in Jinja templates and Django templates.

What works differently in Jinja templates compared to Django templates


Although Jinja uses the same pipe | symbol to apply filters to variables, Jinja filters are technically classified into filters and tests. In Django you just have filters that perform tests (e.g. divisibleby) but in Jinja these type of Django filters are actually 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 such 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 filter 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. The recipe Use built-in and custom filters & tests on Jinja templates (like Django filters) describes in detail Jinja's built-in filters and tests, as well as the process of creating and registering custom Jinja filters and tests.

Context processors

Context processors give Django templates access to a set 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 a context processors and getting access to staple variables, as described in the recipe Use data provided by default Django context processors on Django templates.

However, it's relatively easy to create Jinja global variables to become accessible on all Jinja templates -- and act as context processors -- as described in the recipe Set up data for access on all Jinja templates in Django (like 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, as described toward the end of the recipe Use built-in and custom filters & tests on Jinja templates (like Django filters) and the recipe Use and create extensions on Jinja templates (like Django template library tags).

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. This process is described in the recipe Create reusable Jinja templates.

{% 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 either use the csrf_input variable to replicate the same behavior as the {% csrf_token %} tag (e.g. {{csrf_input}} generates a string like <input type="hidden" name="csrfmiddlewaretoken" value="45654654747487">) or use the csrf_token variable which contains the raw CSRF token (e.g.45654654747487).

{% 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 be aware they are not identical. See Jinja {% for %} statement variables for more details.

{% 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 indicating the iteration was empty. In Jinja {% for %} loops you can use the {% else %} clause as a last argument to generate logic or a message indicating the iteration was empty, this process is described in Jinja {% for %} statement and {% for %} with {% else %}.

{% 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 as described in Jinja cycler function. Or you can use the loop.cycle function available in all {% for %} loops described in the Jinja {% for %} statement and loop.cycle function.

{% 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 as described in Jinja 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, as well as the possibility of creating Jinja global variables to simulate the functionality of these Django tags. See the recipe Use and create extensions on Jinja templates (like Django template library tags) for more details on these projects and how to create your own custom tags, as well as the recipe Set up data for access on all Jinja templates in Django (like Django context processors) for examples of creating Jinja global variables that simulate the functionality of these Django tags.

What are new concepts and features in Jinja templates vs. Django 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) to perform certain actions, 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. 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", you can't do stores.get(var) in Django templates, 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, it 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 the 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 (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.

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.