Templates with Jinja2

Passing data into templates, inheritance with blocks, escaping (and the one line that disables it).

Rendering

from flask import render_template

@app.get("/posts")
def posts():
    items = [{"title": "Hello", "slug": "hello"}]
    return render_template("posts.html", posts=items, user=None)
{# templates/posts.html #}
<h1>Posts</h1>
<ul>
{% for p in posts %}
  <li><a href="{{ url_for('show_post', post_id=p.id) }}">{{ p.title }}</a></li>
{% else %}
  <li>No posts yet.</li>
{% endfor %}
</ul>

{% if user %}
  <p>Signed in as {{ user.name }}</p>
{% endif %}

Output is auto-escaped: {{ value }} renders HTML special characters safely, which is what protects you from XSS.

Template inheritance

{# templates/base.html #}
<!DOCTYPE html>
<html lang="en">
<head><title>{% block title %}Site{% endblock %}</title></head>
<body>
  <header>{% include "_nav.html" %}</header>
  <main>{% block content %}{% endblock %}</main>
</body>
</html>
{# templates/posts.html #}
{% extends "base.html" %}
{% block title %}Posts · Site{% endblock %}
{% block content %}
  <h1>Posts</h1>
{% endblock %}
⚠️
{{ value | safe }} disables escaping. Only use it for HTML you generated yourself — never for user input. The same applies to | safe applied to a whole block of user content.

Filters and macros

{{ price | round(2) }}
{{ created_at | datetimeformat }}
{{ text | truncate(80) }}
{{ items | length }}
{{ name | default("anonymous") }}

{% macro field(name, label) %}
  <label for="{{ name }}">{{ label }}</label>
  <input id="{{ name }}" name="{{ name }}">
{% endmacro %}

Register your own filters with @app.template_filter — it keeps display formatting out of view functions.

FAQ

Where does Flask look for templates?
A templates/ folder beside the app module (or the package root for an application factory). Pass template_folder= to change it.
How do I avoid repeating navigation markup?
Put it in the base template or an {% include %} partial. Use {% block %} for the parts that differ.

Your first Flask app Forms, sessions and errors

Last refreshed 2026-09-18.