Template inheritance lets you define a shared layout once in base.html, then have each child page write only the content that differs — instead of copying the whole HTML frame into every template. In Flask, you use {% extends %} to inherit and {% block %} to mark the regions that can be overridden.

This pays off as soon as a project has three or more templates sharing a header, footer, or sidebar.

The Problem

Every page on a site usually shares the same frame: a head tag with shared CSS, a navigation bar, and a footer. If you copy that whole frame into each template file, every menu change or new CSS file means editing dozens of places — slow and easy to miss. Worse, one page drifting away from the shared frame makes the interface inconsistent.

<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<title>Trang chủ</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<header><nav><!-- menu repeated on every page --></nav></header>
<main>
<h1>Xin chào!</h1>
<p>Nội dung riêng của trang chủ.</p>
</main>
<footer>© 2026 My App</footer>
</body>
</html>

The header and footer alone take up most of the file, while the real content is just two lines. The more pages you have, the larger the share of duplicated code — and the harder it is to maintain.

The Solution

Jinja2 — the template engine Flask ships with — solves this with inheritance: the parent file defines the frame and its block regions, and each child file only overrides the blocks it needs to change. Create templates/base.html:

<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<title>{% block title %}My App{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
{% block head %}{% endblock %}
</head>
<body>
<header><nav><!-- menu written exactly once --></nav></header>
<main>{% block content %}{% endblock %}</main>
<footer>© 2026 My App</footer>
</body>
</html>

A child page only declares the inheritance and overrides its own blocks, in templates/page.html:

{% extends "base.html" %}
{% block title %}Trang chủ{% endblock %}
{% block content %}
<h1>Xin chào!</h1>
<p>Nội dung riêng của trang chủ.</p>
{% endblock %}

The view stays the same — you still render the child template as usual, and Flask loads the parent automatically through extends:

from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def home():
return render_template("page.html")

When you want to keep the parent block’s content and only add to it, use {{ super() }} inside the child block — for example, adding a page-specific CSS file while keeping the shared link in the head block:

{% block head %}
{{ super() }}
<link rel="stylesheet" href="{{ url_for('static', filename='home.css') }}">
{% endblock %}

The shared frame now lives in exactly one place: change the menu or add shared CSS in one file and every page picks it up, while child pages keep full control over each region through block.

Apply It to Your Project

Apply this as soon as a project has two or three templates or more: split out a base.html holding the shared frame, and let child pages write only their own content. For repeated pieces that are not layout — a product card or a login form, for example — use {% include %} or a macro instead of turning them into blocks. Keep the number of blocks moderate: too many small blocks make the parent file hard to read.

Related Posts

Frequently Asked Questions

How is template inheritance different from {% include %}?

{% include %} inserts a whole file at the exact place it is called. Inheritance works the other way around: the child page inherits the parent file’s entire frame and only overrides individual regions with block.

Can one template inherit from multiple base files?

No, Jinja2 allows extends with a single file only. To combine several shared pieces, use include or macros for the smaller parts.

What happens if I forget to define a block in the parent file?

A child page overriding a block that does not exist in the parent raises no error — that content is silently discarded. Check carefully that block names match in both files.

When should I use super()?

When you want to keep the parent block’s original content and then add new content. For example: adding page-specific CSS to the head block while keeping the shared links from the parent file.

Does the child template still need a render_template call?

Yes. The view still renders the child template as usual; Flask loads the parent file automatically through extends.

💬 Have you tried this in your own project? If you hit an error or know a shorter way to do it, leave a comment below — I read them all and will add the best ideas to the post.

References