5. Routing and URL parameters

A Flask app grows by adding routes. Each route gives the browser another URL it can ask for, and each URL runs a Python function. This page covers fixed URLs, URLs with values inside them, and url_for(), the Flask helper that builds links from route names.

Static routes

A static route matches one exact URL. You already wrote the home route:

app.py
@app.route("/")
def home():
    return "Home page"

You can add more fixed pages the same way:

app.py
@app.route("/about")
def about():
    return "About this project"


@app.route("/status")
def status():
    return "Music Time Machine is running"

Static routes are best when the URL names a page, not a changing piece of data.

Dynamic routes

A dynamic route captures part of the URL and passes it into the function as an argument. Use angle brackets around the changing part:

app.py
@app.route("/tracks/<track_id>")
def track_detail(track_id):
    return f"Track ID: {track_id}"

If the browser opens /tracks/abc123, Flask runs track_detail("abc123"). The value from the URL becomes a normal Python variable inside the function.

Dynamic routes are useful when the URL identifies a specific thing: one track, one artist, one saved snapshot, one user, or one document.

Type converters

By default, Flask captures URL values as strings. If you need a number, add a type converter:

app.py
@app.route("/snapshots/<int:year>/<int:month>")
def monthly_snapshot(year, month):
    return f"Snapshot for {year}-{month}"

Now /snapshots/2026/6 passes two integers into the function. If someone opens /snapshots/june/latest, Flask does not run the function because the URL does not match the route. It returns a 404 instead.

Common converters include:

  • <name> captures a string without slashes.
  • <int:id> captures an integer.
  • <float:value> captures a floating-point number.
  • <path:filename> captures a path that may include slashes.

When to use a dynamic route

Use a dynamic route when the value is required for the page to make sense:

  • /tracks/<track_id> means "show this track".
  • /artists/<artist_id> means "show this artist".
  • /snapshots/<int:year>/<int:month> means "show this month's snapshot".

Optional filters, searches, and sorting controls belong in query strings, such as /search?q=radiohead. Those come later on the forms and request-data page.

Why hardcoded links become a problem

If you write links by hand, every URL becomes something you have to maintain yourself:

templates/includes/nav.html
<nav>
  <a href="/">Home</a>
  <a href="/about">About</a>
  <a href="/status">Status</a>
</nav>

This works at first. The problem appears when a route changes. If /status becomes /health, every hardcoded link to /status has to be found and edited by hand.

Generate links with url_for()

Flask's url_for() builds a URL from the view function name. That means your templates can point at the Python function instead of spelling out the path manually.

app.py
from flask import Flask

app = Flask(__name__)


@app.route("/")
def home():
    return "Home page"


@app.route("/status")
def status():
    return "Music Time Machine is running"
templates/includes/nav.html

The names passed to url_for() are function names: home and status. If you later change @app.route("/status") to @app.route("/health"), the template can stay the same because the function is still called status.

Build URLs with dynamic values

For dynamic routes, pass the captured values as keyword arguments:

app.py
@app.route("/tracks/<track_id>")
def track_detail(track_id):
    return f"Track ID: {track_id}"
templates/home.html

  View track

That generates /tracks/abc123. The route defines the shape of the URL; url_for() fills in the values.

Use url_for() in redirects too

The same helper works in Python code. When one route sends the browser to another route, use redirect(url_for(...)) instead of a hardcoded string:

app.py
from flask import redirect, url_for


@app.route("/old-status")
def old_status():
    return redirect(url_for("status"))

From this point on, use url_for() for internal links, redirects, and static files. It keeps your app easier to change as the route list grows.