Include reusable template fragments (partials) in your templates.
Basic Include
{% include "partials/header.html" %}
<main>
Content here
</main>
{% include "partials/footer.html" %}
Context Inheritance
Included templates have access to the current context, including loop variables and block-scoped{% set %}variables:
{# page.html #}
{% set user = get_current_user() %}
{% include "partials/user-card.html" %}
{# partials/user-card.html #}
<div class="user-card">
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
</div>
Loop Variables in Includes
Loop variables from{% for %}are visible inside included templates:
{# page.html #}
{% for item in items %}
{% include "partials/item-card.html" %}
{% end %}
{# partials/item-card.html — item and loop are available #}
<div class="card">
<span>{{ loop.index }}.</span>
<h3>{{ item.name }}</h3>
</div>
This works for nested loops and tuple-unpacked variables too:
{% for key, value in entries %}
{% include "partials/entry.html" %}
{% end %}
Passing Variables
Pass specific variables withwith:
{% include "components/button.html" with text="Click Me", url="/action" %}
{# components/button.html #}
<a href="{{ url }}" class="button">{{ text }}</a>
Isolated Context
Useonlyto include with an empty context:
{% include "components/widget.html" with title="Widget" only %}
The included template only sees title, not the parent context.
Ignore Missing
Skip if the template doesn't exist:
{% include "optional/sidebar.html" ignore missing %}
With fallback:
{% include ["theme/header.html", "default/header.html"] %}
Kida tries each template in order, using the first one found.
Relative Paths
Resolve includes against the current template's directory with./ and ../:
{# pages/about.html #}
{% include "./_hero.html" %} {# → pages/_hero.html #}
{% include "../shared/nav.html" %} {# → shared/nav.html #}
Relative paths also work inside {% extends %}, {% embed %}, and {% from ... import ... %}. This lets you move a folder without touching any references inside it.
Absolute (root-relative) names like"components/card.html"keep working unchanged — relative resolution is purely additive.
Paths that walk above every loader search root raiseTemplateNotFoundError. Relative names passed directly to env.get_template()from Python also raise — relative resolution only makes sense from inside a template.
Namespace Aliases
For cross-cutting libraries that are not locality-bound — shared components, layouts — configuretemplate_aliases on the Environment and reference them with an @alias/prefix:
from kida import Environment, FileSystemLoader
env = Environment(
loader=FileSystemLoader("templates/"),
template_aliases={
"components": "ui/components",
"layouts": "ui/layouts",
},
)
{# Any template, anywhere in the tree: #}
{% extends "@layouts/base.html" %} {# → ui/layouts/base.html #}
{% include "@components/card.html" %} {# → ui/components/card.html #}
{% from "@components/nav.html" import nav %}
{% embed "@layouts/shell.html" %}{% end %}
Aliases resolve before loader lookup, so the same @components/ prefix works from any folder without the caller knowing the physical path. Unknown aliases raise TemplateNotFoundErrorand list the configured aliases.
Aliases and relative paths are separate resolution modes — aliases always resolve to an absolute root, so@components/./foo is not a composition, just a no-op ./segment after substitution.
Dynamic Includes
Include based on a variable:
{% include component_name %}
{# Or with string concatenation (+ is supported; ~ is the explicit coercing form) #}
{% include "components/" + widget_type + ".html" %}
{% include "components/" ~ widget_type ~ ".html" %}
Include vs Extends
| Aspect | {% include %} |
{% extends %} |
|---|---|---|
| Purpose | Embed a fragment | Inherit structure |
| Context | Shares parent context | Isolated |
| Blocks | Cannot override blocks | Overrides blocks |
| Use case | Components, partials | Page layouts |
Common Patterns
Component Library
templates/
├── components/
│ ├── button.html
│ ├── card.html
│ ├── modal.html
│ └── nav.html
└── pages/
└── home.html
{# pages/home.html #}
{% include "components/nav.html" %}
<main>
{% for item in items %}
{% include "components/card.html" %}
{% end %}
</main>
Conditional Includes
{% if user.is_admin %}
{% include "admin/toolbar.html" %}
{% end %}
{% if show_sidebar %}
{% include "partials/sidebar.html" %}
{% end %}
Loop Includes
{% for post in posts %}
{% include "partials/post-card.html" %}
{% end %}
The loop variable post and loop context are automatically visible in the included template. You can still use withto pass additional variables if needed:
{% for post in posts %}
{% include "partials/post-card.html" with show_date=True %}
{% end %}
Performance
Included templates are cached just like regular templates. The compilation cost is paid once, then reused.
# Templates are compiled once and cached
env = Environment(loader=FileSystemLoader("templates/"))
env.cache_info() # Shows template cache stats
See Also
- Refactor-Safe Templates — Tutorial on moving folders without breaking includes, using
.//../and@alias/prefixes. - Inheritance — Template inheritance
- Functions — Reusable functions
- Loading Templates — Template loaders