Template Inheritance

Difficulty: Beginner

Template inheritance is Jinja2's most powerful feature. It lets you define a base template with the common structure of your site (header, navigation, footer) and then extend it in child templates that only override specific blocks. This follows the DRY (Don't Repeat Yourself) principle and makes maintaining consistent layouts across your application effortless.

The concept works like class inheritance in Python. A base template defines blocks - named sections that child templates can override. A child template declares which base template it extends and then fills in the blocks with specific content. Everything outside of blocks in the child template is ignored.

The base template uses {% block name %}...{% endblock %} to define overridable sections. Common blocks include title, head (for page-specific CSS/meta tags), content (the main page body), and scripts (for page-specific JavaScript). You can place default content inside a block that child templates can override or extend.

Child templates begin with {% extends 'base.html' %} and then use {% block name %} to override specific blocks. The extends tag must be the first tag in the template (not counting comments). Only content inside block tags is used from the child template.

The super() function lets you include the parent block's content while adding to it. This is useful for blocks like head or scripts where you want to keep the parent's content and add page-specific items. Inside a block, {{ super() }} renders the parent's version of that block.

You can nest inheritance multiple levels deep. For example, base.html defines the overall structure, admin/base.html extends it to add an admin sidebar, and admin/users.html extends admin/base.html to fill in the specific content. Each level can add blocks, override blocks, or call super() to extend them.

Block names must be unique within a template. However, Jinja2 allows using the block name in the endblock tag for readability: {% endblock content %}. Blocks can also be used inline for simple overrides: {% block title %}Home{% endblock %}.

Template inheritance reduces code duplication dramatically. Instead of copying the entire HTML structure into every page template, you write it once in the base template. When you need to change the navigation or footer, you change it in one place and every page updates automatically.

Code examples

Base Template

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}My App{% endblock %}</title>
    {% block head %}{% endblock %}
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">Home</a>
        <a href="{{ url_for('about') }}">About</a>
    </nav>

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

    <footer>
        <p>&copy; 2024 My App</p>
    </footer>

    {% block scripts %}{% endblock %}
</body>
</html>

The base template defines the overall page structure with block placeholders. Each block has a name and optional default content. Child templates override these blocks.

Child Template

<!-- templates/home.html -->
{% extends 'base.html' %}

{% block title %}Home - My App{% endblock %}

{% block content %}
    <h1>Welcome Home</h1>
    <p>This is the home page.</p>
    <ul>
    {% for item in items %}
        <li>{{ item }}</li>
    {% endfor %}
    </ul>
{% endblock %}

{% block scripts %}
    <script src="{{ url_for('static', filename='js/home.js') }}"></script>
{% endblock %}

The child template extends base.html and overrides the title, content, and scripts blocks. Everything outside of blocks is ignored. The nav and footer from the base template appear automatically.

Using super() to Extend Blocks

<!-- templates/base.html -->
<head>
    <title>{% block title %}My App{% endblock %}</title>
    {% block head %}
        <link rel="stylesheet" href="{{ url_for('static', filename='css/base.css') }}">
    {% endblock %}
</head>

<!-- templates/dashboard.html -->
{% extends 'base.html' %}

{% block title %}Dashboard - {{ super() }}{% endblock %}

{% block head %}
    {{ super() }}
    <!-- Adds to parent's head instead of replacing -->
    <link rel="stylesheet" href="{{ url_for('static', filename='css/dashboard.css') }}">
{% endblock %}

{% block content %}
    <h1>Dashboard</h1>
{% endblock %}

super() inserts the parent block's content. The title becomes 'Dashboard - My App'. The head block includes BOTH base.css (from parent) and dashboard.css (added by child). Without super(), the parent's content is replaced.

Multi-Level Inheritance

<!-- templates/base.html -->
<html>
<body>
    <nav>{% block nav %}Main Nav{% endblock %}</nav>
    <div class="container">
        {% block sidebar %}{% endblock %}
        <main>{% block content %}{% endblock %}</main>
    </div>
</body>
</html>

<!-- templates/admin/base.html -->
{% extends 'base.html' %}

{% block nav %}
    {{ super() }}
    <span>| Admin Panel</span>
{% endblock %}

{% block sidebar %}
    <aside>
        <a href="/admin/users">Users</a>
        <a href="/admin/settings">Settings</a>
    </aside>
{% endblock %}

<!-- templates/admin/users.html -->
{% extends 'admin/base.html' %}

{% block content %}
    <h1>Manage Users</h1>
    <table>...</table>
{% endblock %}

Inheritance can be nested: admin/users.html extends admin/base.html, which extends base.html. The users page gets the main nav, the admin sidebar, and fills in its own content.

Key points

Concepts covered

extends, block, super(), Base Template, DRY Principle