Skip to main content

Customizing the Template Environment

When you need to format dates, check specific object properties, or inject global variables like the current user into every template, you can extend the Jinja2 environment through the App class. Flask provides several decorators and methods to register custom filters, tests, and variables that become available in your templates.

Custom Filters

Filters are functions applied to variables using the pipe symbol (|). You can register them using the @app.template_filter() decorator or the add_template_filter method.

from flask import Flask

app = Flask(__name__)

@app.template_filter("reverse")
def reverse_filter(s):
"""Usage in template: {{ my_string | reverse }}"""
return s[::-1]

# Alternatively, register without a decorator
def shout(s):
return f"{s.upper()}!"

app.add_template_filter(shout, name="shout")

If you don't provide a name to the decorator, the function's name is used as the filter name in Jinja.

Custom Tests

Tests are used to evaluate expressions and return a boolean, typically used with the is keyword. Register them with @app.template_test() or add_template_test.

import math

@app.template_test("prime")
def is_prime_test(n):
"""Usage in template: {% if value is prime %}...{% endif %}"""
if n < 2:
return False
for i in range(2, int(math.ceil(math.sqrt(n))) + 1):
if n % i == 0:
return False
return True

Global Variables and Functions

Global variables or functions are available directly in the template context without needing to be passed to render_template. Use @app.template_global() or add_template_global.

@app.template_global()
def double(n):
"""Usage in template: {{ double(10) }}"""
return 2 * n

Context Processors

Context processors are functions that run before a template is rendered and return a dictionary of variables to be merged into the template context. This is the preferred way to inject dynamic data, like the current user or a navigation menu, into all templates.

@app.context_processor
def inject_user_data():
# This dictionary's keys become variables in the template
return {
"user": get_current_user(),
"is_admin": check_admin_status()
}

Context processors are defined in the Scaffold class and inherited by App. When registered on the application instance, they affect every template rendered by the app.

Low-level Environment Configuration

You can customize the underlying Jinja2 Environment by modifying jinja_options. This must be done before the jinja_env property is accessed for the first time, as the environment is cached.

app = Flask(__name__)

# Change the tags used for variables
app.jinja_options.update(
variable_start_string="[[",
variable_end_string="]]",
)

The App class uses jinja_options when calling create_jinja_environment. Common options include trim_blocks, lstrip_blocks, and autoescape.

Troubleshooting

Setup Finished Error

All customizations (filters, tests, globals, and context processors) must be registered during the application setup phase. If you attempt to register them after the application has handled its first request, Flask will raise an AssertionError.

# This will fail if called after the app starts handling traffic
def late_registration():
app.add_template_filter(my_func) # Raises AssertionError

This is enforced by the _check_setup_finished method in flask.sansio.app.App, which ensures that the application configuration remains consistent once it begins serving requests.

Template Auto-Reload

If your custom filters or templates are not updating during development, ensure TEMPLATES_AUTO_RELOAD is configured. By default, it follows the DEBUG flag.

app.config["TEMPLATES_AUTO_RELOAD"] = True

When debug is set to True on the App instance, it automatically sets jinja_env.auto_reload to True unless explicitly overridden in the config.