Jinja2 Templates — Rendering HTML Pages

In this chapter, you will learn to use the Jinja2 template engine to render complete HTML pages and extract common layouts.


From strings to templates

In the previous chapter, view functions directly returned strings with HTML tags embedded in the lines, which could not be reused and were difficult to maintain.

render_template()It is the entry point for Flask's template rendering: it loads HTML files, injects variables, and returns a complete HTML response.

Example

# File path: app.py
from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")
def index():
    # render_template('template file name', variable name=value, ...)
    return render_template('index.html', title='EXAMPLE Blog')

Flask by default, in the project root directory'stemplates/Find templates in the folder.


Create template files

Create in the project root directorytemplates/folder, then createindex.html。

Example

<!-- File path: templates/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
</head>
<body>
    <h1>{{ title }}</h1>
    <p>Welcome, this is a page rendered with the Jinja2 template engine.</p>
</body>
</html>

Jinja2 template syntax overview

Jinja2 syntax is almost exactly the same as Django template syntax, and is also similar to Vue3's interpolation syntax.

PurposeJinja2 / DjangoDescription
Output variables{{ variable }}Supports attribute access: {{ user.name }}
Loops{% for item in list %}...{% endfor %}Supports loop.index
Conditions{% if cond %}...{% endif %}Chainable: {% elif %}
Inheritance{% extends 'base.html' %}Must be on the first line
Block definitions{% block name %}...{% endblock %}Child templates fill in the gaps of the parent template

Jinja2 and Django template syntax are almost identical, but Jinja2 is more flexible: you can write more complex Python expressions in templates, whereas Django templates deliberately restrict some capabilities to avoid business logic leaking into templates.


Template inheritance: base.html

Multiple pages share the navigation bar and footer, useTemplate inheritanceAvoid duplicate HTML.

Creating a base.html parent template

Example

<!-- File path: templates/base.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- block title: child template can override the page title -->
    <title>{% block title %}EXAMPLE Blog{% endblock %}</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: #f5f5f5;
            color: #333;
        }
        .navbar {
            display: flex; justify-content: space-between; align-items: center;
            padding: 16px 40px; background: #fff;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        .navbar .logo { font-size: 22px; font-weight: bold; color: #e74c3c; text-decoration: none; }
        .navbar a { margin-left: 20px; text-decoration: none; color: #333; }
        .container { max-width: 960px; margin: 40px auto; padding: 0 20px; }
        .footer { text-align: center; padding: 20px; color: #999; border-top: 1px solid #eee; }
    </style>
    {% block extra_head %}{% endblock %}
</head>
<body>
    <header class="navbar">
        <a href="/" class="logo">EXAMPLE Blog (Flask)</a>
        <nav>
            <a href="/">Home</a>
            <a href="/about">About</a>
        </nav>
    </header>

    <main class="container">
        <!-- block content: child template fills in the page main content here -->
        {% block content %}
        {% endblock %}
    </main>

    <footer class="footer">
        <p>© 2024 EXAMPLE Blog. Powered by Flask.</p>
    </footer>
</body>
</html>

Child templates inherit the parent template

Example

<!-- File path: templates/index.html -->
{% extends 'base.html' %}

{% block title %}EXAMPLE Blog - Home{% endblock %}

{% block content %}
<h2>Latest Articles</h2>
<p>The article list will be displayed in later chapters.</p>
{% endblock %}

{% extends 'base.html' %}It must be written on the first line of the child template (except for comments). This is a hard requirement of Jinja2—the template engine needs to determine the inheritance relationship first.


url_for() generates links

Don't hardcode URLs, useurl_for()Look up the address by function name.

Example

# Define the route endpoint in views.py
@app.route("/")
def index(): ...

@app.route("/post/<int:post_id>")
def post_detail(post_id): ...

Example

[mycode4 type="html"> In templates, url_for() generates linksHome Article 3Referencing static files [/mycode4]

The route changed (e.g.,/post/Change to/article/), in the templateurl_for()Automatically generate new addresses.


Static file configuration

Flask by default willstatic/directory as the static file root directory.

blog_project/
├── static/
│   ├── css/
│   │   └── style.css
│   ├── js/
│   │   └── main.js
│   └── images/
│       └── logo.png
├── templates/
└── app.py

In templates, viaurl_for('static', filename='css/style.css')reference.


Chapter summary

In this chapter, you mastered the core of Jinja2 templates: render_template() to render templates, {{ }} to output variables, {% for/if %} control flow, base.html + block + extends template inheritance, and url_for() to generate links.

Now the blog page has become well-structured HTML, and the common parts have been extracted into base.html.

other extensions