Templates with Jinja2

A template is an HTML file with placeholders that Flask fills in with your data at request time — Flask's Jinja2 engine lets you keep your HTML in separate files and inject Python values into them with render_template .

Learn Templates with Jinja2 in our free Flask course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free Flask course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

In this lesson you'll render template files, pass variables into them, and use Jinja2 loops and conditionals to build pages from data. Run the examples locally with flask run .

So far your view functions returned HTML as a Python string. That gets messy fast. The better way is to keep HTML in its own file and ask Flask to render it. Flask uses the Jinja2 template engine and a helper called render_template .

Flask looks for template files inside a folder named templates/ sitting next to your app file. A project might look like this:

You then render index.html by name. The Python below imports render_template and returns a rendered file instead of a string:

Templates become powerful when you pass data into them. You send variables as keyword arguments to render_template , and inside the template you display a value with the double-brace delimiter {'{ }'}. For example {'{ }'} prints whatever you passed as name .

The Python below passes name and visits into a template. The accompanying HTML uses {'{ }'} and {'{ }'} to drop those values into the page:

In prose we write the output delimiter as {'{ }'}, but inside an actual .html template file you type it normally. Jinja2 replaces it with the value at render time.

Jinja2 also runs logic. While {'{ }'} prints a value, the {' '} delimiter controls flow — loops and conditionals. To loop you write {' '} and close it with {' '}. For conditions you use {' '} and {' '}.

The Python below passes a list of tasks into a template. The HTML loops over them and shows a message when the list is empty:

Finish the view so it renders a template and passes a username into it. Replace each ___ in the Python.

❌ jinja2.exceptions.TemplateNotFound: index.html

Flask couldn't find the file. Make sure it's inside a folder named templates/ next to your app file, and that the name you passed matches exactly.

You used {'{ }'} in the template but didn't pass name to render_template . Add it as a keyword argument, e.g. render_template("p.html", name="Ada") .

Lesson 5 complete — your pages come from templates now!

You can render HTML files with render_template , pass variables into them, output values with {'{ }'}, and run loops and conditionals with {' '}. Your HTML now lives where it belongs — in template files.

🚀 Up next: Static Files (CSS, JS, Images) — style those templates and add scripts and images.

Practice quiz

Which template engine does Flask use by default?

  • Mako
  • Handlebars
  • Jinja2
  • Django Templates

Answer: Jinja2. Flask renders templates with the Jinja2 engine.

Which function renders a template file from a view?

  • render_template()
  • send_template()
  • template()
  • render()

Answer: render_template(). render_template('index.html') loads and renders a file from the templates folder.

Where does Flask look for template files?

  • A folder named views/
  • A folder named templates/
  • The static/ folder
  • The project root

Answer: A folder named templates/. Flask looks for templates in a folder named 'templates' next to your app file.

Which Jinja2 delimiter outputs a value?

  • {# value #}

Double curly braces {{ }} print a value into the page.

Which delimiter runs logic such as loops and conditionals?

  • {% ... %}
  • {{ ... }}
  • {# ... #}
  • <% ... %>

Answer: {% ... %}. The {% ... %} delimiter controls flow, for example {% for %} loops and {% if %} conditionals.

How do you pass a variable into a template?

  • By global assignment
  • As a keyword argument to render_template
  • Via request.form
  • Through a config key

Answer: As a keyword argument to render_template. You pass keyword arguments like render_template('hi.html', name='Ada') and reference them by name.

Which tag closes a {% for %} loop in Jinja2?

  • {% endloop %}
  • {% next %}
  • {% endfor %}
  • {% done %}

Answer: {% endfor %}. Every {% for %} must be closed with a matching {% endfor %}.

What does {# note #} do in a template?

  • Outputs the note text
  • Starts a loop
  • Defines a variable
  • Adds a comment that is not rendered

Answer: Adds a comment that is not rendered. {# ... #} is a Jinja2 comment and never appears in the rendered page.

A TemplateNotFound error usually means what?

  • The file is not in the templates/ folder or the name is wrong
  • The variable was not passed
  • The secret key is missing
  • Debug mode is off

Answer: The file is not in the templates/ folder or the name is wrong. Flask could not find the file; it must be inside templates/ and the name must match exactly.

Why use templates instead of returning HTML strings?

  • They render faster than strings always
  • They keep HTML separate from Python code
  • They disable caching
  • They require no folder

Answer: They keep HTML separate from Python code. Templates keep your HTML in its own files, separate from your Python logic, which scales much better.