Template inheritance cho phép bạn định nghĩa layout chung một lần trong file base.html, rồi để các trang con chỉ viết phần nội dung khác nhau, thay vì copy toàn bộ khung HTML vào từng template. Trong Flask, bạn dùng {% extends %} để kế thừa và {% block %} để đánh dấu vùng có thể ghi đè.

Cách này phù hợp ngay khi dự án có từ 3 template trở lên dùng chung header, footer hoặc sidebar.

Vấn đề

Mỗi trang web thường có cùng một khung: thẻ head với CSS chung, thanh điều hướng và footer. Nếu bạn copy toàn bộ khung này vào từng file template, mỗi lần đổi menu hay thêm một file CSS là phải sửa ở hàng chục nơi — vừa mất thời gian vừa dễ sót. Tệ hơn, một trang lệch khỏi khung chung sẽ khiến giao diện không nhất quán.

<!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 lặp lại ở mọi trang --></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>

Chỉ riêng phần header và footer đã chiếm gần hết file, trong khi nội dung thật chỉ có hai dòng. Càng nhiều trang, tỉ lệ code lặp càng lớn và càng khó bảo trì.

Giải pháp

Jinja2 — engine template Flask dùng sẵn — giải quyết bằng kế thừa: file cha định nghĩa khung và các khối block, file con chỉ ghi đè khối cần thay. Tạo file 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 viết một lần duy nhất --></nav></header>
<main>{% block content %}{% endblock %}</main>
<footer>© 2026 My App</footer>
</body>
</html>

Trang con chỉ cần khai báo kế thừa và ghi đè các khối của mình, trong file 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 %}

Phía view không đổi — bạn vẫn render template con như bình thường, Flask tự tải file cha qua extends:

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

Khi cần giữ nội dung gốc của khối cha và chỉ thêm vào, dùng {{ super() }} trong khối con — ví dụ thêm một file CSS riêng mà vẫn giữ link CSS chung trong khối head:

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

Khung chung chỉ tồn tại ở một nơi: đổi menu hay thêm CSS chung thì sửa một file là mọi trang cập nhật theo, trang con vẫn toàn quyền thay đổi từng vùng bằng block.

Áp dụng vào dự án của bạn

Áp dụng ngay khi dự án có từ 2–3 template trở lên: tách một base.html chứa khung chung, các trang con chỉ viết nội dung riêng. Với phần lặp lại nhưng không phải layout — ví dụ card sản phẩm hay form đăng nhập — dùng {% include %} hoặc macro thay vì biến chúng thành block. Giữ số lượng block vừa phải: quá nhiều block nhỏ khiến file cha khó đọc.

Bài viết liên quan

Câu hỏi thường gặp

Template inheritance khác {% include %} ở điểm nào?

{% include %} chèn nguyên một file vào đúng vị trí gọi. Inheritance thì ngược lại: trang con kế thừa toàn bộ khung của file cha và chỉ ghi đè từng vùng bằng block.

Một template có thể kế thừa nhiều file base không?

Không, Jinja2 chỉ cho extends một file duy nhất. Muốn kết hợp nhiều phần chung, dùng include hoặc macro cho phần nhỏ.

Quên định nghĩa block trong file cha thì sao?

Trang con ghi đè một block không tồn tại trong file cha sẽ không báo lỗi — nội dung đó bị bỏ qua âm thầm. Hãy kiểm tra kỹ tên block ở hai file khớp nhau.

Dùng super() khi nào?

Khi muốn giữ nội dung gốc của khối cha rồi thêm nội dung mới. Ví dụ: thêm CSS riêng vào khối head mà vẫn giữ các link chung ở file cha.

Template con có cần gọi render_template không?

Có. View vẫn render template con như bình thường; Flask tự tải file cha qua extends.

💬 Bạn đã thử cách này trong dự án của mình chưa? Nếu gặp lỗi hoặc có cách làm gọn hơn, để lại bình luận bên dưới — mình đọc hết và sẽ bổ sung những ý hay vào bài.

Nguồn tham khảo