Jinja2 Deep Dive: Filters, Macros & Inheritance
Jinja2 is Flask's template engine that turns HTML files into dynamic pages using variables, filters, loops, and reusable layouts.
Learn Jinja2 Deep Dive: Filters, Macros & Inheritance in our free Flask course — a beginner-friendly interactive lesson with worked examples, a practice…
Part of the free Flask course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll go beyond basic output and master filters, control flow, template inheritance, includes, and macros — the tools that keep large template sets clean.
Inside a template, prints a value and runs a statement. A filter transforms the value to its left using the pipe character, like .
Filters chain and accept arguments: default('Guest') supplies a fallback for missing values, length counts items, and join(', ') stitches a list into a string. They keep formatting logic in the template instead of cluttering your Python.
We use render_template_string here so every example runs with no template files — but the exact same syntax works in .html files rendered with render_template .
When a built-in filter doesn't exist, register your own with @app.template_filter . The decorated function receives the piped value (plus any arguments) and returns the transformed result.
Templates also have full control flow: iterates and branches. Together they let you build tables, lists, and conditional sections directly in markup.
Notice loop.index — Jinja exposes a special loop object inside every for block with handy helpers like loop.index , loop.first , and loop.last .
Template inheritance is Jinja's superpower. A base layout declares placeholders; a child template uses and overrides only the blocks it cares about. Your header, nav, and footer live in one place.
drops another template's contents inline — perfect for a reusable card or navbar. A macro is like a template function: define it once, call it many times to render repeated markup such as form inputs.
Because extends and include need to look templates up by name, the example registers them with a DictLoader instead of files — the behavior is identical to a real templates/ folder.
The first print shows the child filling the base layout's blocks; the macro prints two inputs from one definition. That is how you keep a 50-page site DRY.
Complete the template so it shows a default name, counts the tasks, and uppercases a status. Replace each ___ .
Jinja can't locate the parent. Make sure the name matches a file in templates/ (or a key in your loader) exactly, including the .html extension.
Every needs and every needs . A missing end tag raises a TemplateSyntaxError.
Build a macro that renders a product card, then call it for each item in a list.
Lesson complete — your templates are now DRY and powerful!
You can apply built-in and custom filters, loop and branch in markup, share a layout with extends and block , and factor out repetition with include and macros.
🚀 Up next: URL Building with url_for — generate links the right way instead of hardcoding paths.
Practice quiz
In a Jinja2 template, what does {{ ... }} do?
- Defines a macro
- Imports a module
- Prints (outputs) a value or expression
- Starts a loop
Answer: Prints (outputs) a value or expression. {{ ... }} prints a value, while {% ... %} runs a statement.
What does {% ... %} mark in a Jinja2 template?
- A comment
- A raw string
- A printed value
- A statement or control structure
Answer: A statement or control structure. {% ... %} delimits statements like for, if, block, and extends.
Which character applies a filter to a value?
- The pipe |
- The ampersand &
- The at sign @
- The percent %
Answer: The pipe |. Filters use the pipe, as in {{ name|upper }}.
What does the default filter do, e.g. {{ name|default('Guest') }}?
- Counts characters
- Supplies a fallback when the value is undefined
- Uppercases the value
- Reverses the string
Answer: Supplies a fallback when the value is undefined. default('Guest') provides a fallback for a missing/undefined value.
Which filter counts the items in a list?
- count
- size
- length
- total
Answer: length. {{ items|length }} returns the number of items.
How do you register a custom Jinja filter in Flask?
- @app.filter
- @app.add_filter
- @jinja.filter
- @app.template_filter("name")
Answer: @app.template_filter("name"). @app.template_filter("name") registers a reusable custom filter.
Inside a {% for %} loop, what does loop.index give?
- The 1-based position of the current item
- The list length
- The loop variable name
- The last item
Answer: The 1-based position of the current item. loop.index is the 1-based iteration counter Jinja exposes.
Which tag makes a child template build on a base layout?
- {% include %}
- {% extends 'base.html' %}
- {% import %}
- {% macro %}
Answer: {% extends 'base.html' %}. {% extends 'base.html' %} lets a child override the base layout's blocks.
What does {% include '_partial.html' %} do?
- Defines a new block
- Loops over a partial
- Drops another template's contents inline
- Deletes a template
Answer: Drops another template's contents inline. include inserts the rendered contents of another template in place.
What is a Jinja macro best described as?
- A reusable template function you define once and call many times
- A database query
- A Python decorator
- A CSS class
Answer: A reusable template function you define once and call many times. A macro is like a template function for rendering repeated markup.