Jinja2 is a modern, designer-friendly templating engine for Python. It lets you mix plain text (like HTML) with special template syntax to inject dynamic values, perform loops and conditionals, and reuse layouts.
{{ name }}).{% ... %} (if, for, block, extends).Variables are written using double curly braces:
{{ variable_name }} or {{ user.name }}.
Filters transform values and use the pipe syntax:
{{ name|upper }}, {{ items|length }}.
{% if condition %} ... {% endif %}{% for item in items %} ... {% endfor %}{% block content %} ... {% endblock %}{% extends "base.html" %}You can use Jinja2 directly without a web framework by loading templates from the filesystem and rendering them.
from jinja2 import Environment, FileSystemLoader
# 1. Configure where templates live
env = Environment(loader=FileSystemLoader("templates"))
# 2. Load a template file from the templates/ directory
template = env.get_template("hello.html")
# 3. Data (context) to render inside the template
context = {
"name": "Meghraj",
"languages": ["Python", "JavaScript", "C++"],
}
# 4. Render final text output
output = template.render(context)
print(output)
This script looks for a file templates/hello.html, fills in all the placeholders using the context dictionary, and prints the final rendered text. You can then write this to a file, return it as an HTTP response, or send it as an email body.
This is how the corresponding Jinja2 template might look inside templates/hello.html:
# Jinja2 HTML template that renders a greeting page
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello Jinja2</title>
</head>
<body>
<h1>Hello, {{ name }}!</h1>
<h2>Your favourite programming languages:</h2>
<ul>
{% for lang in languages %}
<li>{{ lang }}</li>
{% endfor %}
</ul>
<p>You know {{ languages|length }} languages in total.</p>
</body>
</html>
For the given context, the core part of the rendered output would look like:
<h1>Hello, Meghraj!</h1>
A list of Python, JavaScript, C++, and a line saying You know 3 languages in total.
Flask integrates Jinja2 by default. You place templates in a templates/ folder and use render_template() to render them.
# Basic Flask application using Jinja2 templates
from flask import Flask, render_template
app = Flask(__name__)
# Route for the home page
@app.route("/")
def home():
# Data passed into the Jinja2 template
user = {
"name": "Student",
"is_admin": False,
"skills": ["HTML", "CSS", "Python"]
}
return render_template("profile.html", user=user)
if __name__ == "__main__":
# Enable debug mode only during development
app.run(debug=True)
And the template templates/profile.html could use Jinja2 like this:
# Jinja2 HTML template for a user profile page
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>User Profile</title>
</head>
<body>
<h1>Welcome, {{ user.name }}!</h1>
{% if user.is_admin %}
<p>You have admin privileges.</p>
{% else %}
<p>You are a regular user.</p>
{% endif %}
<h2>Skills</h2>
<ul>
{% for skill in user.skills %}
<li>{{ skill }}</li>
{% endfor %}
</ul>
</body>
</html>
When you visit http://localhost:5000/, Flask passes the user dictionary into profile.html. Jinja2 uses the values to decide which paragraph to show (admin vs regular user), and creates a list of skills dynamically.
upper, lower, length, safe, etc.).
# Base layout template for the site
{# base.html #}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}My Site{% endblock %}</title>
</head>
<body>
<header>
<h1>My Site</h1>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>© {{ 2025 }} My Site</p>
</footer>
</body>
</html>
# Child template that extends base.html and fills in content
{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block content %}
<h2>Home page</h2>
<p>This page uses the base layout and fills in its own content block.</p>
{% endblock %}
auth/login.html) for larger projects.base.html with a header and footer, then create two child templates: home.html and about.html that extend the base./products that passes a list of product dictionaries (name, price, in_stock) to a template and displays them in a table.₹1,234.00 and use it in your template.