Rendering Jinja templates

Flask · Essentials · 12 min

Real pages need layouts, loops and escaping, which hand-built strings get wrong.

Flask uses Jinja for HTML. Put templates in a templates/ folder next to your app module and call render_template("page.html", **variables).

Inside a template:

  • {{ expression }} outputs a value; {{ user.name }} works for attributes and dict keys
  • {% if %}, {% for %} and {% endfor %} provide logic
  • {{ value|filter }} transforms output, such as |title, |length or |default("none")

Autoescaping is on for .html, .htm, .xml, .xhtml and .svg templates: <script> in a user's name is rendered as text, which prevents cross-site scripting. Only mark content safe (|safe or Markup) when you generated it and trust it completely.

Layouts use template inheritance. A base.html defines the page skeleton with named blocks; child templates write {% extends "base.html" %} and fill blocks like {% block content %}.

Flask makes a few objects available in every template without passing them: request, session, g, config, url_for() and get_flashed_messages(). You can add your own with a @app.context_processor that returns a dict.

For quick experiments, render_template_string renders a template from a string, with autoescaping always on. In real apps prefer files: they are cached and editors highlight them.

Keep logic in views and presentation in templates. If a template needs complex computation, prepare the data in Python first.

Example

from flask import Flask, render_template_string

app = Flask(__name__)
# In a real app this lives in templates/users.html and you call render_template.
PAGE = """<h1>{{ title|title }}</h1>
<ul>{% for u in users %}<li>{{ u.name }}{% if u.admin %} (admin){% endif %}</li>{% endfor %}</ul>
<p>{{ users|length }} users, path {{ request.path }}</p>"""

@app.get("/users")
def users():
    data = [{"name": "Ann", "admin": True}, {"name": "<script>x</script>", "admin": False}]
    return render_template_string(PAGE, title="team members", users=data)

print(app.test_client().get("/users").text)
# <h1>Team Members</h1>
# <ul><li>Ann (admin)</li><li>&lt;script&gt;x&lt;/script&gt;</li></ul>
# <p>2 users, path /users</p>

Remember

  • render_template("x.html", **vars) renders templates/x.html
  • {{ }} outputs values, {% %} runs logic, |filters transform
  • Autoescaping protects .html templates from XSS; |safe turns it off

Watch out for

  • Using |safe on user-provided content, which reopens the XSS hole autoescaping closed.

Check yourself

  1. A user's name is <b>Bo</b>. What does {{ name }} render in an .html template?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: flask.palletsprojects.com/en/stable/templating/