Django Template Inheritance Explained: DTL Part 2

Django Template Inheritance Explained, Part 1 solved one problem and left another sitting there on purpose. Your homepage now pulls in {{ site_name }} and {{ tagline }} from a real template file instead of a Python string — but that template still has its own full <!DOCTYPE html>, its own <head>, its own navbar. Build a second page with render() the same way, and you’re copying that exact structure into a second file. Build a fifth page, and you’re maintaining five copies of the same navbar.

Template inheritance is what actually fixes that, and it’s the whole subject of this post.

The Idea Behind Inheritance (Django Template Inheritance Explained)

One template holds the shared structure — the <head>, the navbar, the footer. Every other page template extends that one, filling in only the part that’s actually different for that page. Change the navbar once, in one file, and every page using it updates automatically.

Two tags make this work: {% extends %} and {% block %}.

Step 1: Create a Project-Level Templates Folder

Up to now, templates have lived inside pages/templates/, scoped to one app. A base template is meant to be shared across every app in the project, so it doesn’t really belong inside any single app’s folder. The standard place for it is a templates folder at the project root, sitting next to manage.py:

myfirstproject/
    manage.py
    templates/
        base.html
    pages/
        templates/
            pages/
                home.html
    myfirstproject/
        settings.py
Django Template Inheritance Explained

Django doesn’t search this root-level folder automatically — you have to point it there yourself. Open settings.py, find the TEMPLATES setting, and update the DIRS list:

python

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],
        'APP_DIRS': True,
        ...
    },
]
Django Template Inheritance Explained

BASE_DIR is already defined near the top of settings.py by default — it just points to your project’s root folder, the same place manage.py lives. Adding BASE_DIR / 'templates' to DIRS tells Django to also look inside that root templates folder, in addition to each app’s own templates folder through APP_DIRS.

Now create base.html inside that root folder, not inside pages/templates/. Put everything that should appear on every page:

Also Add App In To Installed Apps

Django Template Inheritance Explained

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}My Django Site{% endblock %}</title>
</head>
<body>
    <nav>
        <a href="/">Home</a>
        <a href="/about/">About</a>
    </nav>

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

    <footer>
        <p>&copy; 2026 My Django Site</p>
    </footer>
</body>
</html>
Django Template Inheritance Explained

Those {% block %} tags aren’t logic — they’re not {% if %}, they’re not checking anything. They’re just named placeholders, marking the spots where a child template is allowed to insert its own content.

Step 2: Rewrite home.html to Extend It

Instead of a full HTML document, home.html now only needs to declare what it extends and fill in the blocks:

html

{% extends "base.html" %}

{% block title %}Home{% endblock %}

{% block content %}
    <h1>Welcome to {{ site_name }}</h1>
    <p>{{ tagline }}</p>
{% endblock %}

{% extends "base.html" %} has to be the very first line in the file — Django requires that. Everything below it fills in the named blocks from the base template. The view from Part 1 doesn’t need to change at all; it still passes site_name and tagline through render() exactly as before.

Reload the homepage. Visually, nothing looks different. Structurally, the entire page is now built from two files working together instead of one file repeating everything.

Django Template Inheritance Explained

Step 3: Give the About Page the Same Treatment

Assuming you have an about.html template already, or are creating one now, the pattern is identical:

html

{% extends "base.html" %}

{% block title %}About{% endblock %}

{% block content %}
    <h1>About This Site</h1>
    <p>This page shares the same navbar and footer as the homepage, without repeating any of that HTML here.</p>
{% endblock %}
Django Template Inheritance Explained

Visit /about/, and the navbar, the <head>, the footer — all identical to the homepage, without a single line of that markup existing in this file.

[Screenshot spot: home and about pages open in two tabs, both showing the identical navbar, proving they’re sharing one base file.]

Proving the Point: Change the Navbar Once

This is the part worth actually testing, not just reading about. Open base.html and add a third link:

html

<nav>
    <a href="/">Home</a>
    <a href="/about/">About</a>
    <a href="/contact/">Contact</a>
</nav>

Reload both the homepage and the About page. The new link appears on both, instantly, without touching home.html or about.html at all. That single edit updating every page at once is the entire reason inheritance exists.

Django Template Inheritance Explained

What’s Deliberately Left Out Here

Consistent with where this course actually is, this post doesn’t touch {% if %} or {% for %} — those get their own dedicated lesson separately. There’s no CSS or static file loading yet either. {% extends %} and {% block %} are the only two new pieces of syntax in this post, kept isolated so the mechanics of inheritance don’t get tangled up with anything else.

Mistakes Worth Flagging

Two things trip people up at this stage. First, forgetting {% extends "base.html" %} as the literal first line of a child template — even a comment or blank line above it causes an error. Second, and specific to this setup, forgetting to add BASE_DIR / 'templates' to DIRS in settings.py. Without that line, Django has no idea the root-level templates folder exists, and you’ll get a “TemplateDoesNotExist” error even though the file is sitting right there. If base.html won’t resolve after moving it, that setting is the first thing worth checking.

Series Connections

This continues directly from Part 1 of this series, where render() and {{ variable }} display were introduced — worth revisiting if the context dictionary or render() call here feels unfamiliar. For a deeper look at overriding blocks selectively, including calling a parent block’s original content with {{ block.super }}, the official Django documentation on template inheritance covers patterns beyond what’s needed here.

Convert one more page of your own to extend base.html before the next lesson, then make one small edit to the base file and watch it apply everywhere at once. That’s the moment inheritance stops being a concept and starts being obviously useful.

Full Course

Django Template Inheritance Explained: DTL Part 2

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top
Nima Academy
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.