Displaying a Table
Paragraphs work, but a list of people with multiple fields really belongs in a table. HTML has proper table tags built in — and combining them with the Jinja2 loop gives you a clean, readable result.
HTML table tags
A table is built from a few tags working together:
| Tag | What it does |
|---|---|
<table> |
The table container |
<thead> |
The header row section |
<tbody> |
The data rows section |
<tr> |
A single row (table row) |
<th> |
A header cell (bold, centred by default) |
<td> |
A data cell |
A simple table with two rows looks like this:
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr>
<td>Alice</td>
<td>24</td>
</tr>
</tbody>
</table>
Combining the table with the Jinja2 loop
The header row is fixed — you write it once. The data rows are where the loop goes. Each iteration of the loop produces one <tr> with the person's data in <td> cells.
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Gender</th>
</tr>
</thead>
<tbody>
{% for person in people %}
<tr>
<td>{{ person.name }}</td>
<td>{{ person.age }}</td>
<td>{% if person.gender == 1 %}Male{% else %}Female{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
The loop sits inside <tbody>. Each time it runs, it writes one <tr> with three <td> cells. When it finishes, </tbody> closes.
The complete files
app.py
from flask import Flask, render_template
app = Flask(__name__)
people = [
{"name": "Alice", "age": 24, "gender": 1},
{"name": "Bob", "age": 31, "gender": 2},
{"name": "Carol", "age": 19, "gender": 1},
]
@app.route('/')
def home():
return render_template('index.html', people=people)
if __name__ == '__main__':
app.run(debug=True)
templates/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>People</title>
</head>
<body>
<h1>People</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Gender</th>
</tr>
</thead>
<tbody>
{% for person in people %}
<tr>
<td>{{ person.name }}</td>
<td>{{ person.age }}</td>
<td>{% if person.gender == 1 %}Male{% else %}Female{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
Run the app and visit http://127.0.0.1:5000. You should see a proper table with one row per person.
Challenge
Add two more people to the people list in app.py. Save and refresh. The table grows automatically — you never touched the HTML.
Then add a fourth column to the table — <th>ID</th> in the header and <td>{{ loop.index }}</td> in the loop. loop.index is a Jinja2 built-in that gives you the current row number starting from 1. Does the table now show a numbered ID column?