Static Files, Styling, and Simple Layout
Serve CSS and JS, and link them the right way
In this lesson
Static files are assets like CSS, JavaScript, and images that Flask sends to the browser unchanged, rather than rebuilding them for every visitor. Now that your pages work, this lesson covers where static files belong and how to link them with url_for instead of hardcoding fragile paths.
Explain it like I’m 5
Static files are the decorations and helper scripts your page needs. Python doesn’t rewrite them for each visitor — it just points the browser at them.
Static files are served as-is
CSS styles, JavaScript behaviour, and images don’t change per request, so Flask serves them straight from a static/ folder without running any code. Keeping them there (not in templates/) is what marks them as static assets.
myapp/
app.py
templates/
index.html
static/
style.css
Link static files with url_for
Rather than hardcoding /static/style.css, you ask Flask to build the path with url_for("static", filename="style.css"). That keeps links correct even if the app moves, and avoids typos.
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
def url_for_static(filename):
return f"/static/{filename}"
print(url_for_static("style.css"))
print(url_for_static("app.js"))
/static/style.css /static/app.js
Build the HTML <link> tag that loads static/style.css. Use the helper to get the /static/style.css path for the href.
def static_url(filename):
return f"/static/{filename}"
# TODO: build <link rel="stylesheet" href="/static/style.css">
link = ""
print(link)
Call static_url("style.css") to get /static/style.css, then drop it into the href with an f-string.
def static_url(filename):
return f"/static/{filename}"
link = f'<link rel="stylesheet" href="{static_url("style.css")}">'
print(link)
<link rel="stylesheet" href="/static/style.css">
Common mistake: Hardcoding the /static/... path incorrectly
Typing the path by hand is quick, until a small typo breaks every link.
Use url_for("static", filename="...") so Flask builds the correct path and keeps it right if the app’s mount point changes.
Common mistake: Putting CSS in the templates folder
It’s all “web stuff,” so the folders blur together.
Stylesheets and scripts belong in static/. Only files Python renders go in templates/.
Common mistake: Not seeing CSS changes because of the browser cache
Browsers cache static files, so an edited stylesheet may not show.
Do a hard refresh (or disable cache in dev tools) after editing CSS. The change is saved; the browser is showing an old copy.
What belongs in the static/ folder?
static/ holds assets sent unchanged; rendered pages live in templates/.
Why use url_for("static", ...) instead of hardcoding the path?
url_for generates the right URL, avoiding brittle hand-typed paths.
What does CSS change about a page?
CSS controls how the page looks; it doesn’t change the server-side logic.
Mini exercise (medium)
Write link_tag(filename) that returns a full stylesheet link tag for a static file — e.g. link_tag("main.css") returns <link rel="stylesheet" href="/static/main.css">.
Do it here. Finish the code below and press Run to try your answer right in the browser — no setup needed.
def link_tag(filename):
# TODO: return a <link rel="stylesheet" href="/static/FILENAME">
return ""
print(link_tag("main.css"))
Return f'<link rel="stylesheet" href="/static/{filename}">' — mind the quotes by using single quotes around the whole f-string.
def link_tag(filename):
return f'<link rel="stylesheet" href="/static/{filename}">'
print(link_tag("main.css"))
<link rel="stylesheet" href="/static/main.css">
assert link_tag("main.css") == '<link rel="stylesheet" href="/static/main.css">', "point href at /static/main.css"
assert link_tag("a/b.css") == '<link rel="stylesheet" href="/static/a/b.css">', "keep the filename path intact after /static/"
print("✓ Stylesheet linked!")