Repeated HTML snippets like form fields and cards should be written once and called everywhere.
A macro is a template function:
{% macro field(name, label, type="text", value="") %}
<label>{{ label }} <input name="{{ name }}" type="{{ type }}" value="{{ value }}"></label>
{% endmacro %}Call it like a function: {{ field("email", "Email", type="email") }}. Macros have default arguments, and extra keyword arguments are available in kwargs if the macro references it.
A {% call %} block passes a chunk of template to a macro, which renders it with {{ caller() }}. That is how you write wrappers such as cards or modals around arbitrary content.
To share macros across files:
{% import "forms.html" as forms %}then{{ forms.field(...) }}{% from "forms.html" import field, button as btn %}
Include renders another template in place: {% include "partials/nav.html" %}. ignore missing skips absent files, and a list tries each name in order.
The context rules differ, which trips people up:
includepasses the current context by default, so the partial sees your variables.importdoes not pass the context by default (imported templates are cached), so macros in it can't see page variables unless you writeimport ... with context. Prefer passing values as macro arguments instead.
Guideline: macros for parametrized components, includes for static chunks like footers, inheritance for overall page layout.
Example
from jinja2 import DictLoader, Environment
env = Environment(autoescape=True, trim_blocks=True, lstrip_blocks=True, loader=DictLoader({
"ui.html": """{% macro field(name, label, type="text") -%}
<label>{{ label }} <input name="{{ name }}" type="{{ type }}"></label>
{%- endmacro %}
{% macro card(title) -%}
<div class="card"><h2>{{ title }}</h2>{{ caller() }}</div>
{%- endmacro %}""",
"nav.html": "<nav>Hi {{ user }}</nav>",
"page.html": """{% import "ui.html" as ui %}
{% include "nav.html" %}
{% call ui.card("Sign up") %}{{ ui.field("email", "Email", type="email") }}{% endcall %}""",
}))
print(env.get_template("page.html").render(user="Ann"))
# <nav>Hi Ann</nav><div class="card"><h2>Sign up</h2><label>Email <input name="email" type="email"></label></div>Remember
- Macros are template functions with arguments and defaults
- {% call %} + caller() pass a content block into a macro
- include sees the current context; import doesn't unless with context
Watch out for
- Expecting macros from an imported file to see the page's variables.