Right, so you’ve got your base template set up. It’s beautiful. It’s got your <head>, your <footer>, your nav bar that finally works right. You’re feeling like a web development wizard. And then your PM walks over and says, “This is great! Now, can we make the blog post pages look totally different? And the landing pages need a unique layout. Oh, and the admin panel should be a completely separate experience.”

Your first, perfectly sane instinct is to start adding a million {% if %} statements to your one glorious base template. Don’t. You’ll end up with a Frankenstein’s monster of a file that’s impossible to debug. I’ve been there. It smells of fear and desperation.

The smarter move is to wield multiple base templates. The core idea is simple: different sections of your site often have fundamentally different DNA. A public-facing blog post and a logged-in user dashboard might share some elements (a footer, maybe), but their overall structures—their grids, their columns, their entire raison d’être—are completely different. Trying to force them to share a single parent template is like making a sports car and a minivan share the same chassis. It’s a compromise that pleases no one.

The Nested Base Template Pattern

This is your bread and butter. You start with your absolute, bottom-of-the-barrel, common-to-every-single-page template. I call this base.html. It’s not sexy, but it’s essential.

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}My Awesome Site{% endblock %}</title>
    <!-- Your common CSS and JS includes go here -->
</head>
<body>
    <!-- Your universal header, present on EVERY page -->
    <header>Site Logo and Nav</header>

    <!-- This is the main event. A huge block for child templates to fill. -->
    <main>
        {% block content %}{% endblock %}
    </main>

    <!-- Your universal footer, also on every page -->
    <footer>Copyright etc.</footer>
</body>
</html>

Now, for your blog section, you realize every page needs a sidebar for recent posts and a specific CSS file. Instead of polluting the main base.html, you create a new base template that extends the root one.

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

<!-- Load any static files or tags specific to the blog -->
{% load static %}

{% block extra_css %}
    <!-- This appends to the head block from the parent, not replaces it! -->
    <link rel="stylesheet" href="{% static 'blog/css/blog.css' %}">
{% endblock %}

{% block content %}
    <!-- This is the key: we're defining a new two-column structure -->
    <div class="blog-container">
        <div class="blog-main-content">
            <!-- A new, more specific block for the *actual* page content -->
            {% block blog_content %}{% endblock %}
        </div>
        <aside class="blog-sidebar">
            {% block blog_sidebar %}
                <!-- Default sidebar content -->
                <h3>Recent Posts</h3>
                ...
            {% endblock %}
        </aside>
    </div>
{% endblock %}

See what we did there? We didn’t just override the content block; we redefined it with a whole new layout and, crucially, created new, more specific blocks (blog_content, blog_sidebar) within it. Now, an individual blog post template is clean and simple:

<!-- templates/blog/post_detail.html -->
{% extends "blog/base_blog.html" %}

{% block title %}{{ post.title }}{% endblock %}

{% block blog_content %}
    <article>
        <h1>{{ post.title }}</h1>
        <div>{{ post.body|safe }}</div>
    </article>
{% endblock %}

<!-- We don't even have to touch blog_sidebar; it uses the parent's default -->

The Radical Split (A.K.A. The “Clean Break”)

Sometimes, sections are so different that sharing even the universal base.html is a bad idea. Your admin panel is the classic example. It probably has its own CSS framework, its own navigation, and no need for your public-facing footer.

In this case, you just… make a new one. No extends. No inheritance. It’s a declaration of independence.

<!-- templates/admin/base_admin.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Admin Panel - {% block admin_title %}Dashboard{% endblock %}</title>
    <!-- Link to your Bootstrap Admin or whatever dark theme you're using -->
</head>
<body class="admin-body">
    <nav class="admin-nav">...</nav>
    <div class="admin-wrapper">
        {% block admin_content %}{% endblock %}
    </div>
    <!-- Notice: no public footer here -->
</body>
</html>

This feels radical, but it’s the right choice when the two experiences share almost nothing. It prevents you from having to add {% if not request.user.is_staff %} to your main footer. Trust me, that way leads to madness.

Why This Isn’t Just Neat and Tidy

This isn’t about organization for its own sake. It’s about reducing cognitive load and minimizing bugs.

  • Scope Control: Blocks in base_blog.html are completely irrelevant to anyone using base_admin.html. You can’t accidentally override a block that doesn’t exist in your chosen parent.
  • Performance (The Real Kind): Developer performance. You can hand templates/blog/ to a front-end dev and say, “Here’s your sandbox. Go nuts.” They don’t need to understand the entire site’s structure, just the blog base template.
  • Maintenance: Need to change the sidebar across every blog page? You change it in one file (base_blog.html), not in fifty individual post templates. This is the whole point of templating, and using multiple bases applies the principle correctly.

The pitfall to avoid is over-engineering. Don’t create a new base template for every single page. Create one when you have a clear, structural divergence. If it’s just a couple of elements that are different, a well-placed {% include %} or a single {% if %} statement inside your main base template might be simpler. But when the layout itself changes course, that’s your cue to break out a new base template. Your future self, debugging at 2 AM, will thank you for it.