YouTip LogoYouTip

Flask Blog Jinja2 Templates

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 inline, which could not be reused and were difficult to maintain.

render_template() is Flask's entry point for rendering templates: it loads HTML files, injects variables, and returns complete HTML responses.

Example

# File path: app.py

from flask import Flask, render_template

app = Flask( __name__ )

@app.route("/")

def index():

    # render_template('template_filename', variable_name=value, ...)

    return render_template('index.html', title='TUTORIAL Blog')

Flask looks for templates in the templates/ folder in the project root directory by default.


Creating Template Files

Create a templates/ folder in the project root directory, then create index.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 page is rendered with the Jinja2 template engine.</p>

</body>

</html>

Jinja2 Template Syntax Quick Reference

Jinja2 syntax is almost identical to Django template syntax, and also similar to Vue3's interpolation syntax.

Purpose Jinja2 / Django Description
Output variable {{ variable }} Supports attribute access: {{ user.name }}
Loop {% for item in list %}...{% endfor %} Supports loop.index
Condition {% if cond %}...{% endif %} Chainable: {% elif %}
Inheritance {% extends 'base.html' %} Must be on the first line
Block definition {% block name %}...{% endblock %} Child templates fill in slots in parent templates

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


Template Inheritance: base.html

Multiple pages share navigation bars and footers; use template inheritance to avoid repeating HTML.

Creating the 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 templates can override the page title -->

    <title>{% block title %}TUTORIAL 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">TUTORIAL Blog (Flask)</a>

        <nav>

            <a href="/">Home</a>

            <a href="/about">About</a>

        </nav>

    </header>

    <main class="container">

        <!-- block content: child templates fill in page main content here -->

        {% block content %}

        {% endblock %}

    </main>

    <footer class="footer">

        <p>Β© 2024 TUTORIAL Blog. Powered by Flask.</p>

    </footer>

</body>

</html>

Child Template Inheriting from Parent

Example

<!-- File path: templates/index.html -->

{% extends 'base.html' %}

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

{% block content %}

    <h2>Latest Articles</h2>

    <p>Article list will be shown in subsequent chapters.</p>

{% endblock %}

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


url_for() Generating Links

Don't hardcode URLs; use url_for() to reverse lookup addresses by function name.

Example

# Define route endpoints in views.py

@app.route("/")

def index(): ...

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

def post_detail(post_id): ...

Example

[mycode4 type="html">

<a href="{{ url_for('index') }}">Home</a>

<a href="{{ url_for('post_detail', post_id=3) }}">Article 3</a>

<img src="{{ url_for('static', filename='images/logo.png') }}" alt="Image 1">


When routes change (e.g., /post/ to /article/), url_for() in templates automatically generates new addresses.


Static File Configuration

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

blog_project/
β”œβ”€β”€ static/
β”‚   β”œβ”€β”€ css/
β”‚   β”‚   └── style.css
β”‚   β”œβ”€β”€ js/
β”‚   β”‚   └── main.js
β”‚   └── images/
β”‚       └── logo.png
β”œβ”€β”€ templates/
└── app.py

Reference in templates via url_for('static', filename='css/style.css').


Chapter Summary

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

Now the blog pages have become clearly structured HTML, with common parts extracted into base.html.

← Flask Blog Views BlueprintDjango Blog Deploy β†’