Templates

View saved

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.html from every page that should share the chrome
  • Prefer url_for for 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.