What Is Jinja2?
Jinja2 is the default templating engine bundled with Flask. Instead of building HTML strings by hand inside your view functions, you write .html files in the templates/ folder that contain plain markup plus special Jinja2 delimiters: {{ ... }} for printing expressions, {% ... %} for statements like loops and conditionals, and {# ... #} for comments. Flask's render_template() function loads a named template, injects the variables you pass as keyword arguments, and returns the rendered string as the HTTP response body.
Cricket analogy: Just as a scorecard template on a cricket broadcast has fixed labels ('Striker', 'Overs', 'Run Rate') with live numbers dropped in each ball, Jinja2 keeps the HTML skeleton fixed while {{ }} slots fill in per-request data like a player's live strike rate.
Variables and Expressions
Inside {{ }}, Jinja2 evaluates expressions much like Python: you can access dictionary keys and object attributes with the same dot syntax (user.name works whether user is a dict or an object, because Jinja2 tries attribute access first, then falls back to item access), call functions, index lists, and apply filters with the pipe operator, such as {{ title|upper }} or {{ price|round(2) }}. Filters like default, length, truncate, and escape are chainable, so {{ bio|truncate(100)|escape }} first shortens the string and then HTML-escapes it. Jinja2 auto-escapes all rendered strings by default when the .html extension is used, which prevents XSS by converting characters like < and & into their HTML entities unless you explicitly mark content safe with the |safe filter or Markup.
Cricket analogy: Like Cricinfo's ball-by-ball commentary that pipes a raw event through formatting steps — abbreviate the bowler's name, then bold the word 'SIX' — chained Jinja2 filters such as truncate|escape transform a raw value through a pipeline before it hits the page.
<!-- templates/product.html -->
<h1>{{ product.name }}</h1>
<p>Price: ${{ product.price|round(2) }}</p>
<p>{{ product.description|truncate(150)|escape }}</p>
<p>Rating: {{ product.rating|default('Not yet rated') }}</p>
Control Structures: Loops and Conditionals
Jinja2 statements use {% %} tags and always require an explicit closing tag: {% if condition %}...{% elif %}...{% else %}...{% endif %} and {% for item in items %}...{% endfor %}. Inside a for loop, Jinja2 exposes a special loop variable with useful attributes such as loop.index (1-based position), loop.index0 (0-based), loop.first, loop.last, and loop.length, which are commonly used for numbering rows or adding a separator only between items rather than after the last one. Whitespace control modifiers like {%- for item in items -%} trim surrounding newlines and indentation so the rendered HTML doesn't accumulate extra blank lines from every loop iteration.
Cricket analogy: Like an umpire counting each delivery in an over and signaling when it's the sixth and final ball, loop.last inside a {% for %} lets a template detect the final iteration to render something differently, such as omitting a trailing comma.
Jinja2's {% for %} loop also supports an {% else %} clause: {% for item in items %}...{% else %}<p>No items found.</p>{% endfor %} runs the else block only when items is empty, which is a clean way to handle empty search results without a separate {% if %} check.
Template Inheritance
Rather than duplicating the <html>, <head>, and navigation markup on every page, Jinja2 supports inheritance through {% block %} tags. A base.html defines named blocks such as {% block title %}Default Title{% endblock %} and {% block content %}{% endblock %}, and a child template starts with {% extends "base.html" %} and overrides only the blocks it needs, for example {% block content %}<h1>Dashboard</h1>{% endblock %}. Calling {{ super() }} inside a child block lets you append to the parent's content instead of fully replacing it, which is useful for extending a base block's CSS or script includes rather than overwriting them.
Cricket analogy: Like a franchise like Mumbai Indians reusing the same kit design (base template) every season but swapping the sponsor logo and player names (blocks) year to year, {% extends %} reuses a base layout while overriding specific sections.
Block names must be unique within a template, and {% extends %} must be the very first tag in a child template — any content, even whitespace outside a block, placed before or alongside {% extends %} at the top level (other than another block) is silently ignored during rendering, which is a common source of confusion for beginners.
- Jinja2 is Flask's default templating engine, invoked via
render_template()on files in thetemplates/folder. {{ }}prints expressions,{% %}executes statements, and{# #}writes comments that never reach the output.- Filters transform values with the pipe syntax and are chainable, e.g.
{{ name|upper|truncate(10) }}. - Auto-escaping is on by default for
.htmltemplates, protecting against XSS unless content is explicitly marked|safe. - The
loopvariable inside{% for %}exposesindex,index0,first, andlastfor position-aware rendering. - Template inheritance via
{% extends %}and{% block %}lets child templates reuse and override a shared base layout. {% extends %}must be the first tag in a child template, and{{ super() }}appends to rather than replaces a parent block.
Practice what you learned
1. Which Jinja2 delimiter is used to print the value of an expression?
2. By default, how does Jinja2 handle a variable containing `<script>` when rendered in an `.html` template?
3. Inside a `{% for %}` loop, which loop attribute is True only on the final iteration?
4. What must be the first tag in a child template that uses template inheritance?
5. What does `{{ bio|truncate(100)|escape }}` do?
Was this page helpful?
You May Also Like
Flask Forms with WTForms
Use Flask-WTF and WTForms to define form fields declaratively, validate user input server-side, and protect submissions with CSRF tokens.
Flash Messages
Use Flask's flash() system to show one-time notifications like success and error alerts to users after a redirect, using session storage under the hood.
Static Files in Flask
Understand how Flask serves CSS, JavaScript, and images from the static folder, how to reference them safely with url_for, and how to prepare static assets for production.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics