Templates
Goal for this part
Replace bare response strings with real HTML pages. You will use Jinja2 templates, a shared base.html layout, and two pages: a home feed and an about page.
After this snapshot, Campus Wire should look like a small website instead of a single line of text. Navigation links should work, and the home page should list a few sample posts passed in from Python.
What you should already know
You should be able to run the Getting Started snapshot and recognize a Flask route. Basic HTML tags help a lot here: headings, paragraphs, links, and a simple page structure.
You do not need to be a CSS expert. This teaching app uses a CDN stylesheet so you can focus on how templates and routes work together.
Why templates matter
Returning long HTML strings from Python becomes messy quickly. Templates keep markup in files under templates/, where you can read and edit the page structure like ordinary HTML with a few extra tags.
render_template loads a template file, fills in variables, and returns the finished HTML. Separating presentation from route logic is one of the habits that makes Flask apps maintainable as they grow.
Walkthrough: shared layout with blocks
base.html holds the shell: navigation, shared CSS, and a content container. Child pages declare {% extends "base.html" %} and fill named blocks such as title and content.
Use url_for('home') (and similar) for links instead of hard-coding paths. Flask builds the URL from the endpoint name, so if a route path changes later, your navigation can stay correct.
{% block content %}{% endblock %}
# Child pages:
{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block content %}
<h1>Latest posts</h1>
{% for post in posts %}
<article>...</article>
{% endfor %}
{% endblock %}
Walkthrough: pass data from the view
The home route builds a list of dictionaries and passes it into the template as posts. Inside the template, a for loop walks that list and prints each title, author, and body.
The about route needs no extra data: it only renders a static page that still inherits the shared layout. Both routes import render_template from Flask.
from flask import Flask, render_template
app = Flask(__name__)
POSTS = [
{"title": "Welcome to Campus Wire", "author": "Alex", "body": "..."},
]
@app.route("/")
def home():
return render_template("home.html", posts=POSTS)
@app.route("/about")
def about():
return render_template("about.html")
How to run it and what you should see
Use the same clone-and-venv pattern as before, this time inside 02-Templates. After python app.py, open the home page. You should see a styled layout with navigation and a short list of sample posts.
Click Through to the About page. The header and footer should stay familiar while the main content changes. If a template is missing, Flask’s debugger will usually name the file it could not find—read that message carefully.
git clone https://github.com/michaeldunga1/fcc-flask-blog.git
cd fcc-flask-blog/02-Templates
python3 -m venv .venv
source .venv/bin/activate
pip install -r ../requirements.txt
python app.py
Common mistakes, troubleshooting, and try this
TemplateNotFound almost always means the file is not under templates/, is misspelled, or you started the app from a different working directory. Jinja errors about undefined variables usually mean the view forgot to pass a name the template expects.
Try adding a third dictionary to POSTS and confirm it appears on the home page. Then change the About page copy inside its template only—no Python change required—to reinforce where content lives.
- Keep HTML in
templates/, not as giant Python strings - Extend
base.htmlfrom every page that should share the chrome - Prefer
url_forfor internal links - Refresh after edits; restart if the server did not auto-reload
Next: Forms
Comments
One comment per signed-in account. Comments are saved with this page’s URL.