Flask Passing Data to Templates
Flask sends data from Python to HTML templates using keyword arguments in render_template(). The data can be strings, numbers, lists, dictionaries, or any Python object. Templates receive the data as variables and display it dynamically.
The Delivery Truck Analogy
Think of render_template() as a delivery truck. The template (HTML file) is the destination. The keyword arguments are the packages on the truck. The template unpacks each package and places the contents where needed.
Python: render_template('page.html', name='Alice', score=98)
│
Flask loads page.html
│
Delivers: name = 'Alice'
score = 98
│
Template: "Hello, {{ name }}! Your score: {{ score }}"
Output: "Hello, Alice! Your score: 98"
Passing a String
@app.route('/greet/<name>')
def greet(name):
return render_template('greet.html', username=name)In greet.html:
<h1>Hello, {{ username }}!</h1>Passing a List
@app.route('/shopping')
def shopping():
cart = ['Apples', 'Bread', 'Milk', 'Eggs']
return render_template('shopping.html', items=cart)In shopping.html:
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>Output:
• Apples • Bread • Milk • Eggs
Passing a Dictionary
@app.route('/profile')
def profile():
user = {
'name': 'Alice',
'age': 30,
'city': 'London'
}
return render_template('profile.html', user=user)In profile.html:
<h2>{{ user.name }}</h2>
<p>Age: {{ user.age }}</p>
<p>City: {{ user.city }}</p>Jinja2 accesses dictionary keys using dot notation, the same as object attributes.
Passing a List of Dictionaries
This is the most common pattern in real applications — sending a list of records from a database to display as a table or card grid:
@app.route('/employees')
def employees():
staff = [
{'name': 'Alice', 'role': 'Engineer', 'salary': 85000},
{'name': 'Bob', 'role': 'Designer', 'salary': 75000},
{'name': 'Carol', 'role': 'Manager', 'salary': 92000},
]
return render_template('employees.html', employees=staff)In employees.html:
<table>
<thead>
<tr><th>Name</th><th>Role</th><th>Salary</th></tr>
</thead>
<tbody>
{% for emp in employees %}
<tr>
<td>{{ emp.name }}</td>
<td>{{ emp.role }}</td>
<td>${{ emp.salary | format_number }}</td>
</tr>
{% endfor %}
</tbody>
</table>Passing Multiple Variables at Once
You can pass as many keyword arguments as needed:
@app.route('/dashboard')
def dashboard():
return render_template(
'dashboard.html',
username='Alice',
notifications=7,
recent_orders=['Order #101', 'Order #102'],
account_status='active'
)Using a Dictionary to Pass Many Variables Cleanly
When passing many variables, collecting them in a dictionary and using ** to unpack keeps the code readable:
@app.route('/dashboard')
def dashboard():
context = {
'username': 'Alice',
'notifications': 7,
'recent_orders': ['Order #101', 'Order #102'],
'account_status': 'active'
}
return render_template('dashboard.html', **context)The **context unpacks the dictionary into keyword arguments. The template receives the same variables either way.
Template Context Processors
Some data belongs on every page — the logged-in user's name, the current year for the copyright notice, or global site settings. Passing these in every single route call is repetitive. Flask's context_processor decorator injects variables into every template automatically:
from datetime import datetime
@app.context_processor
def inject_year():
return {'current_year': datetime.now().year}Now every template can use {{ current_year }} without the route explicitly passing it.
Route passes: render_template('page.html', title='Home')
context_processor adds: current_year=2024
Template receives: title='Home', current_year=2024
Undefined Variables
If a template references a variable that was not passed, Jinja2 renders it as an empty string by default. No error appears in the browser. To catch missing variables during development, check that every template variable appears in the route's render_template() call.
Summary
Data travels from Python to templates through keyword arguments in render_template(). Pass strings, numbers, lists, dictionaries, and objects. Templates access the data using Jinja2's {{ }} syntax and dot notation. For data that every page needs, use a context processor to inject it once rather than repeating it in every route function.
