Table of Contents
Understanding Routing in Flask
In web development with Flask, routing decides which Python code runs when someone visits a specific URL (like /, /about, /hello/jane, etc.).
In the Flask introduction, you saw a very simple route. Here, we will:
- Define multiple routes
- Use dynamic parts in routes (like
/user/<name>) - Restrict types in routes (like
/post/<int:id>) - Return different responses from different routes
Everything below assumes you already have a basic Flask app set up.
Defining Routes with `@app.route()`
Flask uses a decorator, @app.route(), to connect URLs to Python functions.
Basic example with two routes:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
return "Welcome to the homepage!"
@app.route("/about")
def about():
return "This is the about page."
if __name__ == "__main__":
app.run(debug=True)- Visiting
http://127.0.0.1:5000/showsWelcome to the homepage! - Visiting
http://127.0.0.1:5000/aboutshowsThis is the about page.
Each route:
- Uses
@app.route("path") - Has a function that returns what should be shown in the browser
Dynamic Routes: Variables in the URL
Sometimes you want part of the URL to be dynamic, like:
/user/alice/user/bob/hello/John
You can capture part of the URL as a variable:
from flask import Flask
app = Flask(__name__)
@app.route("/hello/<name>")
def hello(name):
return f"Hello, {name}!"
if __name__ == "__main__":
app.run(debug=True)Now:
http://127.0.0.1:5000/hello/Alice→Hello, Alice!http://127.0.0.1:5000/hello/Bob→Hello, Bob!
Here name comes from the URL and is passed to the hello function as an argument.
Type-Constrained Variables in Routes
By default, dynamic parts are strings. You can also specify types:
<int:id>— only matches integers<float:price>— only matches floating-point numbers<path:subpath>— matches a whole path segment that may include/
Example:
from flask import Flask
app = Flask(__name__)
@app.route("/post/<int:post_id>")
def show_post(post_id):
# post_id is an integer here
return f"This is post number {post_id}"
if __name__ == "__main__":
app.run(debug=True)Now:
/post/5→ works,post_idis5/post/abc→ does not match this route (Flask returns 404)
Another example with path:
@app.route("/files/<path:filename>")
def show_file(filename):
return f"You requested the file: {filename}"/files/report.txt→filename = "report.txt"/files/images/logo.png→filename = "images/logo.png"
Named Routes with `url_for()`
Instead of hard-coding URLs as strings, Flask lets you build URLs from the function name using url_for.
This is useful when:
- You change the URL path later
- You want to avoid typos in multiple places
Example:
from flask import Flask, url_for
app = Flask(__name__)
@app.route("/")
def index():
home_url = url_for("index")
about_url = url_for("about")
return f"Home URL: {home_url}, About URL: {about_url}"
@app.route("/about")
def about():
return "About page"
if __name__ == "__main__":
app.run(debug=True)
url_for("index") will return / and url_for("about") will return /about.
You’ll see url_for used a lot inside templates next.
Introduction to Templates
So far, we returned simple text from routes. In real web apps, we usually send HTML pages. Flask uses templates (HTML with placeholders) to do that.
Templates let you:
- Separate Python code from HTML layout
- Insert dynamic content (like user names, lists, etc.) into HTML
- Reuse common parts (like headers, footers, navigation bars)
Flask uses a template engine called Jinja2, but you don’t need to remember the name now—just the idea: HTML + special syntax for dynamic parts.
Setting Up a `templates` Folder
Flask looks for templates in a folder called templates by default.
Typical structure:
your_project/
app.py
templates/
index.html
about.htmlapp.py— your Flask apptemplates/— all your template files go here
Rendering Templates with `render_template()`
To return an HTML template from a route, use render_template.
Example:
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def home():
return render_template("index.html")
@app.route("/about")
def about():
return render_template("about.html")
if __name__ == "__main__":
app.run(debug=True)
index.html and about.html must be in the templates folder.
Example index.html:
<!DOCTYPE html>
<html>
<head>
<title>My First Flask Page</title>
</head>
<body>
<h1>Welcome!</h1>
<p>This page is served by Flask using a template.</p>
</body>
</html>
Now when you open / in your browser, you see this HTML page.
Passing Data from Routes to Templates
Templates become powerful when you send data from your route to the template.
Use render_template("file.html", name=value, ...) to pass values.
Example: passing a name to show in the HTML.
app.py:
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/hello/<name>")
def hello(name):
return render_template("hello.html", username=name)
if __name__ == "__main__":
app.run(debug=True)
templates/hello.html:
<!DOCTYPE html>
<html>
<head>
<title>Hello Page</title>
</head>
<body>
<h1>Hello, {{ username }}!</h1>
<p>Nice to see you here.</p>
</body>
</html>Here:
- In Python, you pass
username=name - In the HTML, you use
{{ username }}to insert its value
This {{ ... }} syntax is part of the template language.
Template Syntax Basics
Inserting Variables: `{{ ... }}`
Use double curly braces to print values.
Examples in template.html:
<p>Your name is: {{ name }}</p>
<p>Your age is: {{ age }}</p>
<p>Welcome, {{ user_first_name }} {{ user_last_name }}!</p>Python route:
@app.route("/user")
def user():
return render_template(
"user.html",
name="Alice",
age=30,
user_first_name="Alice",
user_last_name="Smith"
)
The template engine replaces {{ ... }} with the actual values.
Simple Logic: `{% ... %}`
You can add simple control structures inside templates using {% ... %}. These are not printed; they control the flow.
Common ones:
if,elif,elseforloops
`if` Example
Route:
@app.route("/status/<int:score>")
def status(score):
return render_template("status.html", score=score)
Template status.html:
<!DOCTYPE html>
<html>
<head>
<title>Status</title>
</head>
<body>
<h1>Your score: {{ score }}</h1>
{% if score >= 50 %}
<p>Congratulations, you passed!</p>
{% else %}
<p>Sorry, you did not pass.</p>
{% endif %}
</body>
</html>`for` Loop Example
Route:
@app.route("/shopping-list")
def shopping_list():
items = ["Apples", "Bread", "Milk"]
return render_template("shopping.html", items=items)
Template shopping.html:
<!DOCTYPE html>
<html>
<head>
<title>Shopping List</title>
</head>
<body>
<h1>Shopping List</h1>
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
</body>
</html>Here:
{% for item in items %}starts a loop{% endfor %}ends the loop{{ item }}displays each element
Connecting Routing and Templates
Routing and templates work together:
- Routes receive the request (URL, parameters, form data, etc.)
- Routes prepare data
- Routes call
render_templateto send that data into an HTML template - Templates display data using
{{ ... }}and simple logic using{% ... %}
A slightly more complete example:
app.py:
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
# some data to show on the homepage
title = "My Flask Site"
description = "This is a very simple website built with Flask."
features = ["Fast", "Simple", "Fun to learn"]
return render_template(
"index.html",
page_title=title,
page_description=description,
features=features
)
if __name__ == "__main__":
app.run(debug=True)
templates/index.html:
<!DOCTYPE html>
<html>
<head>
<title>{{ page_title }}</title>
</head>
<body>
<h1>{{ page_title }}</h1>
<p>{{ page_description }}</p>
<h2>Features</h2>
<ul>
{% for feature in features %}
<li>{{ feature }}</li>
{% endfor %}
</ul>
</body>
</html>
Visit / to see how routing and templates work together.
Template Inheritance (Basic Idea)
As your site grows, many pages share the same structure:
- Same header
- Same navigation bar
- Same footer
You don’t want to repeat the same code in every template. Instead, you can:
- Create a base template with the main layout
- Let other templates extend it and fill in certain parts
Simple example (a taste of the idea):
templates/base.html:
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}My Site{% endblock %}</title>
</head>
<body>
<header>
<h1>My Site</h1>
<nav>
<a href="{{ url_for('home') }}">Home</a> |
<a href="{{ url_for('about') }}">About</a>
</nav>
</header>
<main>
{% block content %}
<!-- Default content -->
{% endblock %}
</main>
<footer>
<p>Footer text here</p>
</footer>
</body>
</html>
templates/home.html:
{% extends "base.html" %}
{% block title %}Home - My Site{% endblock %}
{% block content %}
<h2>Welcome to the homepage</h2>
<p>This content is specific to the home page.</p>
{% endblock %}
app.py:
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def home():
return render_template("home.html")
@app.route("/about")
def about():
return render_template("about.html") # you’d define about.html similarly
if __name__ == "__main__":
app.run(debug=True)Here:
base.htmldefines the general layout andblockshome.htmluses{% extends "base.html" %}and fills intitleandcontent
This helps keep your templates clean and consistent.
Using `url_for` Inside Templates
Just like in Python code, you can use url_for inside templates to avoid hard-coding links.
Example in base.html:
<nav>
<a href="{{ url_for('home') }}">Home</a>
<a href="{{ url_for('about') }}">About</a>
</nav>Make sure the function names in your routes match:
@app.route("/")
def home():
return render_template("home.html")
@app.route("/about")
def about():
return render_template("about.html")
Now if you ever change /about to /info, you only update the route in Python—links in the templates still work because they use url_for('about'), not the raw string /about.
Putting It All Together: A Tiny Multi-Page Site
Folder structure:
my_site/
app.py
templates/
base.html
home.html
about.html
app.py:
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def home():
features = ["Simple", "Lightweight", "Beginner-friendly"]
return render_template("home.html", features=features)
@app.route("/about")
def about():
return render_template("about.html")
if __name__ == "__main__":
app.run(debug=True)
templates/base.html:
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}My Flask Site{% endblock %}</title>
</head>
<body>
<header>
<h1>My Flask Site</h1>
<nav>
<a href="{{ url_for('home') }}">Home</a> |
<a href="{{ url_for('about') }}">About</a>
</nav>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>© 2025 My Flask Site</p>
</footer>
</body>
</html>
templates/home.html:
{% extends "base.html" %}
{% block title %}Home - My Flask Site{% endblock %}
{% block content %}
<h2>Welcome</h2>
<p>This is the homepage.</p>
<h3>Features</h3>
<ul>
{% for f in features %}
<li>{{ f }}</li>
{% endfor %}
</ul>
{% endblock %}
templates/about.html:
{% extends "base.html" %}
{% block title %}About - My Flask Site{% endblock %}
{% block content %}
<h2>About</h2>
<p>This is a tiny website built with Flask routes and templates.</p>
{% endblock %}Visiting:
/shows the home page with a features list/aboutshows the about page- The navigation links work using
url_for
This is the basic pattern you’ll use for building real web applications with Flask: routes decide what data to send, and templates decide how to display it.