When building web applications with FastAPI and Jinja2 templates, one of the most common issues developers face is template code duplication. Every HTML page tends to have similar boilerplate code: DOCTYPE declarations, meta tags, CSS links, JavaScript imports, and basic HTML structure. Writing this repeatedly across multiple templates is not only tedious but also makes maintenance a nightmare.
Today, let's explore how template inheritance in Jinja2 can transform your messy, repetitive templates into clean, maintainable code using the power of base.html
.
The Problem: Code Duplication Everywhere
Here's what our templates/blogs/list.html file looks as of now:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<title>Blog List</title>
</head>
<body>
<h1 class="text-3xl font-bold text-center">Blog List</h1>
</body>
</html>
Let us extract out the common parts in a new file named templates/base.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
{% block title %}
{% endblock title %}
{% block head %}
{% endblock head %}
</head>
<body>
{% block body %}
{% endblock body %}
</body>
</html>
Understanding Jinja2 Template Inheritance
Key Concepts
1. {% extends %}
Tag
{% extends "base.html" %}
This tells Jinja2 that this template inherits from base.html
. It must be the first tag in your template.
2. {% block %}
Tags
{% block title %}
<title>Custom Title</title>
{% endblock title %}
Blocks are placeholders in the base template that child templates can override. The base template defines the blocks, and child templates provide the content.
The New list.html:
Now, our blog list page becomes incredibly simple:
{% extends "base.html" %}
{% block title %}
<title>Blog List</title>
{% endblock title %}
{% block body %}
<h1 class="text-3xl font-bold text-center">Blog List</h1>
{% endblock body %}
What we are doing is injecting the contents inside the base.html and doing a final rendering of the HTML.
Common block names include:
title
- for page titlesbody
orcontent
- for main contentscripts
- for page-specific JavaScriptextra_head
- for additional head elementssidebar
- for sidebar content