Bootstrap in Flask
Your patient app works. But it still looks like a plain HTML form with a plain HTML table. One link tag in the template changes that.
Where the link goes
A Flask template is just an HTML file. The Bootstrap CDN link goes in <head> — same as always:
<head>
<meta charset="UTF-8">
<title>Patients</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
</head>
Flask does not know or care that Bootstrap is there. It just serves the HTML. The browser fetches Bootstrap from the internet and applies it before drawing the page.
Before — the plain template
Here is templates/index.html as it looked after the PookieDB in Flask section:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Patients</title>
</head>
<body>
<h1>Add Patient</h1>
<form action="/add" method="post">
<label for="patient_name">Patient Name</label>
<input type="text" id="patient_name" name="patient_name" placeholder="Full name">
<label for="age">Age</label>
<input type="number" id="age" name="age" placeholder="Age">
<button type="submit">Add Patient</button>
</form>
<h2>Patients</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
{% for patient in patients %}
<tr>
<td>{{ patient.name }}</td>
<td>{{ patient.age }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
It works. It looks rough.
After — with Bootstrap
Here is the same template with Bootstrap linked and class names applied:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Patients</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
</head>
<body>
<div class="container mt-4">
<h1 class="mb-4">Add Patient</h1>
<form action="/add" method="post">
<div class="mb-3">
<label for="patient_name" class="form-label">Patient Name</label>
<input type="text" id="patient_name" name="patient_name" class="form-control" placeholder="Full name">
</div>
<div class="mb-3">
<label for="age" class="form-label">Age</label>
<input type="number" id="age" name="age" class="form-control" placeholder="Age">
</div>
<button type="submit" class="btn btn-primary">Add Patient</button>
</form>
<h2 class="mt-5 mb-3">Patients</h2>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
{% for patient in patients %}
<tr>
<td>{{ patient.name }}</td>
<td>{{ patient.age }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
</html>
app.py and database.py did not change at all. Only the template changed — and only with class names and one wrapper <div>. The app behaviour is identical. The page looks completely different.
What each class does
| Class | What it does |
|---|---|
container |
Centres the content and gives it a comfortable max width |
mt-4 |
Margin top — space above the element |
mb-3, mb-4 |
Margin bottom — space below |
mt-5 |
Larger top margin — pushes the Patients heading away from the form |
form-label |
Styles the label to sit cleanly above the input |
form-control |
Styles the input — rounded corners, full width, focus highlight |
btn btn-primary |
A solid blue button |
table |
Basic Bootstrap table — cleaner font, spacing, borders |
table-striped |
Alternating row colours so rows are easy to scan |
table-bordered |
Adds a border around every cell |
Challenge
Open your patient app and replace templates/index.html with the Bootstrap version above. Run python app.py, add a few patients, and see the difference. Then try two things:
- Add
table-hoverto the table classes — hover your mouse over the rows - Change
btn-primarytobtn-success— what colour does that give you?