Documentation Index Fetch the complete documentation index at: https://mintlify.com/pallets/flask/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Flask uses Jinja2 as its template engine, providing powerful template rendering with automatic escaping, inheritance, and integration with Flask’s context system.
Template Basics
Directory Structure
By default, Flask looks for templates in the templates folder:
your_application/
├── app.py
└── templates/
├── base.html
├── index.html
└── user/
└── profile.html
Rendering Templates
From templating.py:136-161, use render_template() to render templates:
from flask import Flask, render_template
app = Flask( __name__ )
@app.route ( '/' )
def index ():
return render_template( 'index.html' )
@app.route ( '/user/<username>' )
def profile ( username ):
user = get_user(username)
return render_template( 'user/profile.html' , user = user)
Jinja2 Environment
Flask creates a customized Jinja2 environment (from app.py:469-507):
def create_jinja_environment ( self ):
options = dict ( self .jinja_options)
if "autoescape" not in options:
options[ "autoescape" ] = self .select_jinja_autoescape
if "auto_reload" not in options:
auto_reload = self .config[ "TEMPLATES_AUTO_RELOAD" ]
if auto_reload is None :
auto_reload = self .debug
options[ "auto_reload" ] = auto_reload
rv = self .jinja_environment( self , ** options)
rv.globals.update(
url_for = self .url_for,
get_flashed_messages = get_flashed_messages,
config = self .config,
request = request,
session = session,
g = g,
)
return rv
Template Context
Automatic Variables
From templating.py:21-33, Flask automatically injects these variables:
{# These are always available #}
{{ config . DEBUG }}
{{ request . method }}
{{ session . user_id }}
{{ g . user }}
{{ url_for ( 'index' ) }}
{{ get_flashed_messages () }}
Passing Variables
Pass data to templates:
@app.route ( '/product/<int:id>' )
def product ( id ):
product = get_product( id )
related = get_related_products( id )
return render_template(
'product.html' ,
product = product,
related = related,
page_title = f ' { product.name } - Store'
)
In the template:
<h1> {{ product . name }} </h1>
<p>Price: $ {{ product . price }} </p>
<h2>Related Products</h2>
{% for item in related %}
<div> {{ item . name }} </div>
{% endfor %}
Template Syntax
Variables
{{ variable }}
{{ user . name }}
{{ items [0] }}
{{ dict [ 'key' ] }}
Control Structures
Conditionals
{% if user . is_authenticated %}
<p>Welcome, {{ user . name }} !</p>
{% elif user . is_guest %}
<p>Welcome, Guest!</p>
{% else %}
<p>Please log in.</p>
{% endif %}
Loops
{% for item in items %}
<li> {{ item . name }} - $ {{ item . price }} </li>
{% else %}
<li>No items found.</li>
{% endfor %}
{# Loop variables #}
{% for user in users %}
{{ loop . index }} : {{ user . name }}
{% if loop . first %} (First) {% endif %}
{% if loop . last %} (Last) {% endif %}
{% endfor %}
Filters
Transform variables:
{{ name | upper }}
{{ text | truncate (20) }}
{{ price | round (2) }}
{{ date | datetimeformat ( '%Y-%m-%d' ) }}
{{ items | length }}
{{ html | safe }} {# Disable escaping #}
Template Inheritance
Base template (base.html):
<!DOCTYPE html>
<html>
<head>
<title> {% block title %} My Site {% endblock %} </title>
{% block head %}{% endblock %}
</head>
<body>
<header>
{% block header %}
<h1>My Site</h1>
{% endblock %}
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
{% block footer %}
© 2024
{% endblock %}
</footer>
</body>
</html>
Child template:
{% extends "base.html" %}
{% block title %} Home - {{ super () }} {% endblock %}
{% block content %}
<h2>Welcome!</h2>
<p>This is the home page.</p>
{% endblock %}
Template Inclusion
Include reusable components:
{# _navigation.html #}
<nav>
<a href=" {{ url_for ( 'index' ) }} ">Home</a>
<a href=" {{ url_for ( 'about' ) }} ">About</a>
</nav>
{# index.html #}
{% include '_navigation.html' %}
{# With variables #}
{% include 'components/card.html' with context %}
{% include 'components/button.html' , label = 'Click Me' , color = 'blue' %}
Macros
Create reusable template functions:
{# macros.html #}
{% macro render_field ( field ) %}
<div class="field">
{{ field . label }}
{{ field ( ** kwargs )| safe }}
{% if field . errors %}
<ul class="errors">
{% for error in field . errors %}
<li> {{ error }} </li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endmacro %}
{# form.html #}
{% from 'macros.html' import render_field %}
<form method="post">
{{ render_field ( form . username ) }}
{{ render_field ( form . password ) }}
</form>
Custom Template Context
Context Processors
From app.py:590-618, add variables to all templates:
@app.context_processor
def inject_user ():
return dict ( current_user = get_current_user())
@app.context_processor
def utility_processor ():
def format_price ( amount ):
return f '$ { amount :.2f} '
return dict ( format_price = format_price)
Use in templates:
<p>Logged in as: {{ current_user . name }} </p>
<p>Price: {{ format_price (19.99) }} </p>
Custom Filters
Register custom Jinja2 filters:
@app.template_filter ( 'reverse' )
def reverse_filter ( s ):
return s[:: - 1 ]
# Or use add_template_filter
def datetimeformat ( value , format = '%H:%M %d -%m-%Y' ):
return value.strftime( format )
app.jinja_env.filters[ 'datetimeformat' ] = datetimeformat
Use in templates:
{{ "hello" | reverse }} {# olleh #}
{{ article . pub_date | datetimeformat }}
{{ article . pub_date | datetimeformat ( '%Y-%m-%d' ) }}
Custom Tests
Create custom template tests:
@app.template_test ( 'prime' )
def is_prime ( n ):
if n < 2 :
return False
for i in range ( 2 , int (n ** 0.5 ) + 1 ):
if n % i == 0 :
return False
return True
Use in templates:
{% if value is prime %}
{{ value }} is a prime number!
{% endif %}
Global Functions
Add custom global functions:
@app.template_global ()
def double ( n ):
return n * 2
# Or use add_template_global
def generate_token ():
return secrets.token_hex( 16 )
app.jinja_env.globals[ 'generate_token' ] = generate_token
Rendering from Strings
From templating.py:151-160, render template strings:
from flask import render_template_string
@app.route ( '/dynamic' )
def dynamic ():
template = '''
<h1>Hello, {{ name }} !</h1>
<p>You have {{ messages|length }} messages.</p>
'''
return render_template_string(
template,
name = 'John' ,
messages = [ 'msg1' , 'msg2' ]
)
Streaming Templates
From templating.py:181-212, stream large templates:
from flask import stream_template
@app.route ( '/large-page' )
def large_page ():
# Returns an iterator
return stream_template(
'large_page.html' ,
items = generate_many_items()
)
Auto-escaping
Flask automatically escapes HTML:
@app.route ( '/safe' )
def safe ():
# This is safe
user_input = '<script>alert("xss")</script>'
return render_template( 'page.html' , content = user_input)
{# Automatically escaped #}
{{ content }} {# <script>... #}
{# Disable escaping (dangerous!) #}
{{ content | safe }} {# <script>... #}
{# Autoescape control #}
{% autoescape false %}
{{ content }}
{% endautoescape %}
Blueprint Templates
Blueprints can have their own template folders:
admin = Blueprint( 'admin' , __name__ ,
template_folder = 'templates' ,
static_folder = 'static' )
@admin.route ( '/' )
def index ():
# Looks in blueprint's template folder first
return render_template( 'admin/index.html' )
From templating.py:49-121, Flask’s DispatchingJinjaLoader searches:
Blueprint template folders
Application template folder
Template Loading
Custom Loader
Customize template loading:
from jinja2 import FileSystemLoader, ChoiceLoader
app.jinja_loader = ChoiceLoader([
FileSystemLoader( 'templates' ),
FileSystemLoader( 'custom_templates' ),
])
Debug Template Loading
Enable template loading explanation:
app.config[ 'EXPLAIN_TEMPLATE_LOADING' ] = True
Best Practices
Template Inheritance Use base templates to avoid repetition
Security First Never disable auto-escaping unless absolutely necessary
Separation of Concerns Keep logic in Python, presentation in templates
Reusable Components Use macros and includes for common components