Skip to main content

Templating and JSON

Flask provides integrated support for rendering HTML templates using the Jinja2 engine and handling JSON data serialization. These features are designed to be extensible, allowing for custom template loaders, context variables, and specialized JSON serialization logic.

HTML Templating with Jinja2

Flask uses Jinja2 as its default templating engine. The integration is primarily handled through the flask.templating module and the Environment class, which extends the standard Jinja2 environment with Flask-specific features.

Rendering Templates

The primary functions for rendering templates are render_template and render_template_string, found in src/flask/templating.py.

  • render_template(template_name_or_list, **context): Renders a template file by name. It looks for the file in the application's template folder or within registered blueprints.
  • render_template_string(source, **context): Renders a template from a raw string.

Example from examples/tutorial/flaskr/blog.py:

@bp.route("/")
def index():
db = get_db()
posts = db.execute(
"SELECT p.id, title, body, created, author_id, username"
" FROM post p JOIN user u ON p.author_id = u.id"
" ORDER BY created DESC"
).fetchall()
return render_template("blog/index.html", posts=posts)

Template Discovery and Blueprints

Flask uses a custom loader called DispatchingJinjaLoader (in src/flask/templating.py) to locate templates. This loader searches for templates in the following order:

  1. The application's own template folder.
  2. The template folders of all registered blueprints, in the order they were registered.

This allows blueprints to provide default templates that can be overridden by the main application.

Extending Template Context

You can make variables or functions available to all templates without passing them manually to every render_template call.

  • Context Processors: Functions decorated with @app.context_processor return a dictionary of variables to be merged into the template context.
  • Template Filters: Functions decorated with @app.template_filter() allow you to transform data within templates (e.g., {{ name|reverse }}).
  • Template Tests: Functions decorated with @app.template_test() allow for custom logic in if statements (e.g., {% if value is boolean %}).
  • Template Globals: Functions decorated with @app.template_global() are available as global functions in templates.

Example of a context processor from tests/test_templating.py:

@app.context_processor
def inject_value():
return {"injected_value": 42}

Streaming Templates

For large responses, stream_template and stream_template_string return an iterator that yields the rendered template in chunks. This is useful for improving perceived performance by sending the initial HTML (like the <head>) before the entire page is rendered.

@app.route("/stream")
def stream():
return stream_template("large_page.html", data=generate_large_data())

JSON Handling

Flask provides the jsonify function and a flexible JSONProvider architecture for handling JSON data.

The jsonify Function

The jsonify function (in src/flask/json/__init__.py) serializes Python objects to JSON and returns a Response object with the application/json mimetype.

Example from examples/javascript/js_example/views.py:

@app.route("/add", methods=["POST"])
def add():
a = request.json["a"]
b = request.json["b"]
return jsonify(result=a + b)

Note: jsonify accepts either positional arguments or keyword arguments, but not both. If multiple positional arguments are provided, they are serialized as a JSON array.

JSONProvider Architecture

JSON behavior is managed by a JSONProvider instance assigned to app.json. By default, Flask uses DefaultJSONProvider (in src/flask/json/provider.py), which uses Python's built-in json module but adds support for several common types:

  • datetime.datetime and datetime.date: Serialized to RFC 822 strings (HTTP date format).
  • uuid.UUID: Serialized to its string representation.
  • dataclasses.dataclass: Serialized using dataclasses.asdict.
  • Markup objects: Objects with an __html__ method (like those from markupsafe) are converted to strings.

Customizing JSON Serialization

You can customize how JSON is handled by subclassing DefaultJSONProvider and assigning it to app.json.

Example of a custom provider from tests/test_json.py:

class CustomProvider(DefaultJSONProvider):
def object_hook(self, obj):
if len(obj) == 1 and "_foo" in obj:
return X(obj["_foo"])
return obj

def loads(self, s, **kwargs):
kwargs.setdefault("object_hook", self.object_hook)
return super().loads(s, **kwargs)

app.json = CustomProvider(app)

Configuration and Debugging

Several configuration options affect templating and JSON behavior:

  • TEMPLATES_AUTO_RELOAD: If enabled, the Jinja environment will check for template source changes and reload them. By default, this is enabled when the app is in debug mode.
  • EXPLAIN_TEMPLATE_LOADING: When set to True, Flask logs detailed information about how it attempts to locate templates, which is invaluable for debugging TemplateNotFound errors.
  • JSON_SORT_KEYS: (Controlled via app.json.sort_keys) Determines if keys in JSON objects are sorted alphabetically. This is True by default in DefaultJSONProvider.

In debug mode, jsonify output is automatically pretty-printed with indentation for better readability. This is controlled by the compact attribute on the JSONProvider.