Skip to main content

Template Inheritance

Template inheritance is one of Jinja2’s most powerful features. It allows you to build a base “skeleton” template that contains common elements of your site and defines blocks that child templates can override.

Why Use Template Inheritance?

  • Reduce duplication: Write common HTML once in a base template
  • Maintain consistency: Ensure all pages share the same structure
  • Easy updates: Change the layout in one place
  • Better organization: Separate layout from content

Basic Inheritance with extends

Child templates use {% extends %} to inherit from a parent template.

Base Template

Create a base template with the common structure:
<!-- 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>
    
    <!-- Common CSS -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    
    <!-- Additional CSS from child templates -->
    {% block stylesheets %}{% endblock %}
</head>
<body>
    <!-- Header -->
    <header>
        <nav>
            <a href="{{ url_for('home') }}">Home</a>
            {% if current_user %}
                <span>Welcome, {{ current_user.name }}!</span>
                <a href="{{ url_for('auth.logout') }}">Logout</a>
            {% else %}
                <a href="{{ url_for('auth.login') }}">Login</a>
            {% endif %}
        </nav>
    </header>
    
    <!-- Flash messages -->
    {% for category, messages in get_flash_messages().items() %}
        {% for message in messages %}
            <div class="alert alert-{{ category }}">
                {{ message }}
            </div>
        {% endfor %}
    {% endfor %}
    
    <!-- Main content area -->
    <main>
        {% block content %}{% endblock %}
    </main>
    
    <!-- Footer -->
    <footer>
        <p>&copy; 2024 My App. All rights reserved.</p>
    </footer>
    
    <!-- Common JavaScript -->
    <script src="{{ asset('js/app.js') }}"></script>
    
    <!-- Additional JavaScript from child templates -->
    {% block scripts %}{% endblock %}
</body>
</html>

Child Template

Child templates extend the base and fill in the blocks:
<!-- templates/user/index.html -->
{% extends "base.html" %}

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

{% block stylesheets %}
    <link href="{{ asset('css/users.css') }}" rel="stylesheet">
{% endblock %}

{% block content %}
    <h1>Users</h1>
    
    <div class="user-list">
        {% for user in users %}
            <div class="user-card">
                <h3>{{ user.name }}</h3>
                <p>{{ user.email }}</p>
                <a href="{{ url_for('user.show', id=user.id) }}">View Profile</a>
            </div>
        {% endfor %}
    </div>
    
    <a href="{{ url_for('user.create') }}" class="btn btn-primary">
        Create New User
    </a>
{% endblock %}

{% block scripts %}
    <script src="{{ asset('js/users.js') }}"></script>
{% endblock %}

Defining Blocks

Blocks are placeholders in the base template that child templates can override.

Defining Blocks

<!-- Base template -->
{% block block_name %}
    Default content (optional)
{% endblock %}

Overriding Blocks

<!-- Child template -->
{% block block_name %}
    New content that replaces the default
{% endblock %}

Block Best Practices

  1. Name blocks descriptively: content, title, stylesheets, scripts
  2. Provide defaults when appropriate: Default titles, empty script blocks
  3. Keep blocks focused: Each block should have a single purpose
  4. Document block purposes: Comment what each block is for

Common Block Patterns

Title Block

<!-- Base template -->
<title>{% block title %}Default Title{% endblock %}</title>

<!-- Child template -->
{% block title %}Page-Specific Title{% endblock %}

Content Block

<!-- Base template -->
<main>
    {% block content %}{% endblock %}
</main>

<!-- Child template -->
{% block content %}
    <h1>My Page Content</h1>
    <p>Content here...</p>
{% endblock %}

Stylesheet Block

<!-- Base template -->
<head>
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    {% block stylesheets %}{% endblock %}
</head>

<!-- Child template -->
{% block stylesheets %}
    <link href="{{ asset('css/custom.css') }}" rel="stylesheet">
{% endblock %}

Script Block

<!-- Base template -->
<script src="{{ asset('js/app.js') }}"></script>
{% block scripts %}{% endblock %}

<!-- Child template -->
{% block scripts %}
    <script src="{{ asset('js/custom.js') }}"></script>
{% endblock %}

Multi-Level Inheritance

You can have multiple levels of inheritance:
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}My App{% endblock %}</title>
    {% block head %}{% endblock %}
</head>
<body>
    {% block body %}{% endblock %}
</body>
</html>
<!-- templates/layouts/dashboard.html -->
{% extends "base.html" %}

{% block body %}
    <div class="dashboard-layout">
        <aside class="sidebar">
            {% block sidebar %}
                <nav>Dashboard Navigation</nav>
            {% endblock %}
        </aside>
        
        <div class="main-content">
            {% block dashboard_content %}{% endblock %}
        </div>
    </div>
{% endblock %}
<!-- templates/user/dashboard.html -->
{% extends "layouts/dashboard.html" %}

{% block title %}User Dashboard{% endblock %}

{% block sidebar %}
    {{ super() }}
    <div class="user-stats">
        <p>Total Users: {{ total_users }}</p>
    </div>
{% endblock %}

{% block dashboard_content %}
    <h1>User Dashboard</h1>
    <p>Manage your users here.</p>
{% endblock %}

Using super()

The super() function lets you render the parent block’s content:
<!-- Base template -->
{% block scripts %}
    <script src="{{ asset('js/app.js') }}"></script>
{% endblock %}

<!-- Child template -->
{% block scripts %}
    {{ super() }}  <!-- Includes app.js from parent -->
    <script src="{{ asset('js/custom.js') }}"></script>
{% endblock %}
This renders:
<script src="/js/app.js?v=a3f8b9c2"></script>
<script src="/js/custom.js?v=b4e9c1d3"></script>

Include Directives

Use {% include %} to insert reusable template fragments:

Creating Partials

<!-- templates/partials/user_card.html -->
<div class="user-card">
    <h3>{{ user.name }}</h3>
    <p>{{ user.email }}</p>
    <a href="{{ url_for('user.show', id=user.id) }}">View Profile</a>
</div>

Including Partials

<!-- templates/user/index.html -->
{% extends "base.html" %}

{% block content %}
    <h1>Users</h1>
    
    {% for user in users %}
        {% include "partials/user_card.html" %}
    {% endfor %}
{% endblock %}

Include with Variables

<!-- Pass specific variables to the included template -->
{% include "partials/alert.html" with context %}

<!-- Or include with specific variables -->
{% include "partials/button.html" with {"label": "Click Me", "type": "primary"} %}

<!-- Include without parent context -->
{% include "partials/footer.html" without context %}

Conditional Includes

{% if show_sidebar %}
    {% include "partials/sidebar.html" %}
{% endif %}

<!-- Ignore missing includes -->
{% include "partials/optional.html" ignore missing %}

Common Template Patterns

Two-Column Layout

<!-- templates/layouts/two_column.html -->
{% extends "base.html" %}

{% block content %}
    <div class="two-column-layout">
        <aside class="sidebar">
            {% block sidebar %}{% endblock %}
        </aside>
        
        <main class="main-content">
            {% block main %}{% endblock %}
        </main>
    </div>
{% endblock %}

Form Layout

<!-- templates/layouts/form.html -->
{% extends "base.html" %}

{% block content %}
    <div class="form-container">
        <h1>{% block form_title %}Form{% endblock %}</h1>
        
        <form method="POST" action="{% block form_action %}{% endblock %}">
            <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
            
            {% block form_content %}{% endblock %}
            
            <button type="submit">{% block submit_text %}Submit{% endblock %}</button>
        </form>
    </div>
{% endblock %}

Error Page Layout

<!-- templates/error/base.html -->
{% extends "base.html" %}

{% block content %}
    <div class="error-page">
        <h1>{% block error_code %}Error{% endblock %}</h1>
        <p>{% block error_message %}Something went wrong{% endblock %}</p>
        <a href="{{ url_for('home') }}">Go Home</a>
    </div>
{% endblock %}
<!-- templates/error/404.html -->
{% extends "error/base.html" %}

{% block title %}404 Not Found{% endblock %}
{% block error_code %}404{% endblock %}
{% block error_message %}Page not found{% endblock %}

Complete Example

Base Template

<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}My App{% endblock %}</title>
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    {% block stylesheets %}{% endblock %}
</head>
<body>
    <nav>
        {% if current_user %}
            <span>{{ current_user.name }}</span>
        {% endif %}
    </nav>
    
    <main>
        {% block content %}{% endblock %}
    </main>
    
    <script src="{{ asset('js/app.js') }}"></script>
    {% block scripts %}{% endblock %}
</body>
</html>

Child Template

<!-- templates/user/show.html -->
{% extends "base.html" %}

{% block title %}{{ user.name }} - My App{% endblock %}

{% block content %}
    <h1>{{ user.name }}</h1>
    <p>Email: {{ user.email }}</p>
    
    <a href="{{ url_for('user.index') }}">Back to Users</a>
{% endblock %}

Next Steps

Build docs developers (and LLMs) love