Inheritance

Template inheritance with extends and blocks

5 min read 985 words

Template inheritance lets you build a base template with common structure and override specific sections in child templates.

Note: Unlike Jinja2, Kida does not supportsuper(). Child blocks fully replace parent content. For "add-to" patterns, define explicit extension blocks in your base template (e.g.,{% block extra_head %}{% end %}).

Base Template

Create a base template with blocks that can be overridden:

{# base.html #}
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}My Site{% end %}</title>
    {% block head %}{% end %}
</head>
<body>
    <header>
        {% block header %}
            <h1>My Site</h1>
            <nav>{% block nav %}{% end %}</nav>
        {% end %}
    </header>

    <main>
        {% block content %}{% end %}
    </main>

    <footer>
        {% block footer %}
            <p>&copy; 2024</p>
        {% end %}
    </footer>
</body>
</html>

Child Template

Extend the base and override blocks:

{# page.html #}
{% extends "base.html" %}

{% block title %}About - My Site{% end %}

{% block content %}
    <h2>About Us</h2>
    <p>Welcome to our site!</p>
{% end %}

Result: The child template inherits all ofbase.html, with thetitleandcontentblocks replaced.

Real-World Examples

Documentation Site

A three-column documentation layout extending a base template:

{# layouts/base.html #}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}Docs{% end %}</title>
    <link rel="stylesheet" href="/css/main.css">
    {% block extra_head %}{% end %}
</head>
<body>
    <header>{% include "partials/nav.html" %}</header>
    <main>{% block content %}{% end %}</main>
    <footer>{% include "partials/footer.html" %}</footer>
    {% block extra_scripts %}{% end %}
</body>
</html>
{# layouts/docs.html #}
{% extends "layouts/base.html" %}

{% block content %}
<div class="docs-layout">
    <nav class="docs-sidebar">
        {% block sidebar %}
            {% include "partials/docs-nav.html" %}
        {% end %}
    </nav>
    <article class="docs-content">
        <h1>{% block page_title %}{% end %}</h1>
        {% block article %}{% end %}
    </article>
    <aside class="docs-toc">
        {% block toc %}{% end %}
    </aside>
</div>
{% end %}
{# pages/getting-started.html #}
{% extends "layouts/docs.html" %}

{% block title %}Getting Started - MyProject{% end %}
{% block page_title %}Getting Started{% end %}

{% block article %}
<p>Welcome to the quick start guide.</p>
{{ content }}
{% end %}

{% block toc %}
<nav class="toc">
    <h3>On this page</h3>
    {{ toc_html }}
</nav>
{% end %}

Extension Blocks Pattern

Since Kida doesn't supportsuper(), use explicit extension blocks to add content without replacing the parent:

{# base.html #}
<head>
    <link rel="stylesheet" href="/css/base.css">
    <link rel="stylesheet" href="/css/theme.css">
    {% block extra_head %}{% end %}  {# Extension point #}
</head>
<body>
    {% block content %}{% end %}
    
    <script src="/js/main.js"></script>
    {% block extra_scripts %}{% end %}  {# Extension point #}
</body>
{# blog/post.html #}
{% extends "base.html" %}

{% block extra_head %}
    {# Adds to head without replacing base styles #}
    <link rel="stylesheet" href="/css/syntax-highlight.css">
    <meta property="og:title" content="{{ post.title }}">
{% end %}

{% block content %}
<article class="blog-post">
    <h1>{{ post.title }}</h1>
    <time>{{ post.date | dateformat }}</time>
    {{ post.content }}
</article>
{% end %}

{% block extra_scripts %}
    {# Adds to scripts without replacing main.js #}
    <script src="/js/syntax-highlight.js"></script>
{% end %}

Blog with Author Layout

Multi-level inheritance for a blog:

{# layouts/base.html #}
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}Blog{% end %}</title>
</head>
<body>
    {% block body %}{% end %}
</body>
</html>
{# layouts/blog.html #}
{% extends "layouts/base.html" %}

{% block body %}
<div class="blog-container">
    <aside class="blog-sidebar">
        {% block sidebar %}
            {% include "partials/recent-posts.html" %}
        {% end %}
    </aside>
    <main class="blog-main">
        {% block main %}{% end %}
    </main>
</div>
{% end %}
{# blog/post.html #}
{% extends "layouts/blog.html" %}

{% block title %}{{ post.title }} - Blog{% end %}

{% block main %}
<article>
    <header>
        <h1>{{ post.title }}</h1>
        <p class="byline">
            By <a href="{{ post.author.url }}">{{ post.author.name }}</a>
            on {{ post.date | dateformat('%B %d, %Y') }}
        </p>
    </header>
    <div class="post-content">
        {{ post.content }}
    </div>
    <footer>
        {% for tag in post.tags %}
            <a href="/tags/{{ tag }}/" class="tag">{{ tag }}</a>
        {% end %}
    </footer>
</article>
{% end %}

Block Scoping

Blocks can be nested:

{# base.html #}
{% block sidebar %}
    <aside>
        {% block sidebar_content %}
            Default sidebar
        {% end %}
    </aside>
{% end %}
{# child.html #}
{% extends "base.html" %}

{% block sidebar_content %}
    Custom sidebar content
{% end %}

Multiple Levels

Inheritance can be multi-level. Each child completely replaces the parent's block:

{# base.html #}
{% block content %}Base{% end %}
{# layout.html #}
{% extends "base.html" %}
{% block content %}
    <div class="layout">Layout content</div>
{% end %}
{# page.html #}
{% extends "layout.html" %}
{% block content %}
    <div class="layout">
        <p>Page content</p>
    </div>
{% end %}

Dynamic Extends

The parent template can be a variable:

{% extends parent_template %}

{% block content %}
    Dynamic parent!
{% end %}
template.render(parent_template="layouts/wide.html")

Block Inside Loops

Blocks cannot be defined inside loops or conditionals. This is intentional—blocks are resolved at compile time.

{# ❌ Invalid #}
{% for item in items %}
    {% block item %}{{ item }}{% end %}
{% end %}

{# ✅ Valid: Use a function instead #}
{% def render_item(item) %}
    <div>{{ item }}</div>
{% end %}

{% for item in items %}
    {{ render_item(item) }}
{% end %}

Best Practices

Descriptive Block Names

{# Good #}
{% block page_title %}{% end %}
{% block sidebar_navigation %}{% end %}
{% block footer_copyright %}{% end %}

{# Avoid #}
{% block b1 %}{% end %}
{% block content2 %}{% end %}

Default Content

Provide sensible defaults in base templates:

{% block meta_description %}
    <meta name="description" content="Default site description">
{% end %}

Template Hierarchy

base.html           ← Site-wide structure
├── docs.html       ← Documentation layout
│   └── tutorial.html  ← Tutorial-specific
├── blog.html       ← Blog layout
│   └── post.html   ← Single post
└── home.html       ← Homepage

See Also