Static Files, Styling, and Simple Layout

Serve CSS and JS, and link them the right way

Intermediate 10 min

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.

Example
myapp/
  app.py
  templates/
    index.html
  static/
    style.css
CSS, JS, and images go in static/; pages go in templates/.

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.

Example
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
In a template, url_for builds the correct /static/... path.
Example
def url_for_static(filename):
    return f"/static/{filename}"

print(url_for_static("style.css"))
print(url_for_static("app.js"))
Output
/static/style.css
/static/app.js
A simplified url_for: it builds the path so you don't hardcode it.

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)

Common mistake: Hardcoding the /static/... path incorrectly

Why it happens:

Typing the path by hand is quick, until a small typo breaks every link.

How to fix it:

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

Why it happens:

It’s all “web stuff,” so the folders blur together.

How to fix it:

Stylesheets and scripts belong in static/. Only files Python renders go in templates/.

Common mistake: Not seeing CSS changes because of the browser cache

Why it happens:

Browsers cache static files, so an edited stylesheet may not show.

How to fix it:

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?

Why use url_for("static", ...) instead of hardcoding the path?

What does CSS change about a page?

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"))

What to learn next

You learned how static files work: CSS, JavaScript, and images live in static/ and are served as-is, and url_for("static", ...) builds their paths so you never hardcode a fragile /static/... link. You wrote link_tag() to generate a stylesheet link.

You now have every piece — routes, templates, forms, and styling. The Unit 6 Project assembles them into a tiny notes app with a testable core. When you’re ready, continue to the project.