Static Files (CSS, JS, Images)
Reviewed & published by Brayan K
Static files are the unchanging assets your site sends to the browser — stylesheets, JavaScript, images, and fonts — and Flask serves them automatically from a special folder named static.
Part of the free Flask course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
In this lesson you'll learn where static files live, how to link them from templates with url_for, and how to style and add behavior to the HTML pages you built in the previous lesson.
What You'll Learn in This Lesson
- What 'static files' means and why Flask treats them specially
- The default static folder layout in a Flask project
- How to link CSS, JS, and images with url_for('static', ...)
- Why url_for beats hard-coding /static/ paths
- How to attach a stylesheet to a Jinja2 template
- How to customize or rename the static folder
1 The Static Folder
A static file is any file that the server sends to the browser unchanged — it isn't generated per request like a Jinja2 template. The most common ones are CSS stylesheets, JavaScript files, and images.
Flask reserves a folder named static for these assets. Any file you put there is automatically reachable at the /static/ URL — you don't write a route for it.
A typical project layout looks like this:
myproject/
├── app.py
├── templates/
│ └── index.html
└── static/
├── style.css
├── script.js
└── logo.pngWith that layout, static/style.css is served at /static/style.css. Run the app with flask run and open that path in your browser to confirm the file loads.
2 Linking Files with url_for
Inside a template you build the URL to a static file with url_for('static', filename='...'). Flask returns the correct path, so your links keep working even if the app is mounted at a subpath later.
The example below defines a route that renders a template, and the template uses url_for to load a stylesheet, a script, and a logo image. The route returns the rendered HTML page to the browser.
# app.py — serve a page that links static files
from flask import Flask, render_template_string
app = Flask(__name__)
# In a real project this HTML lives in templates/index.html.
# We inline it here so you can see the url_for calls clearly.
PAGE = """
<!doctype html>
<html>
<head>
<title>My Styled Page</title>
<!-- Build the URL to static/style.css -->
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<img src="{{ url_for('static', filename='logo.png') }}" alt="Logo">
<h1>Hello, styled Flask!</h1>
<script src="{{ url_for('static', filename='script.js') }}"></script>
</body>
</html>
"""
@app.route("/")
def home():
# render_template_string renders the HTML and resolves every url_for call
return render_template_string(PAGE)
# url_for needs an application context to know where /static lives, and
# test_request_context() gives it one without starting a server:
with app.test_request_context():
from flask import url_for
print("CSS URL:", url_for("static", filename="style.css"))
print("Logo URL:", url_for("static", filename="logo.png"))
# To serve the page for real, end the file with:
# if __name__ == "__main__":
# app.run(debug=True)
# ✅ Expected output:
# CSS URL: /static/style.css
# Logo URL: /static/logo.pngWhen you run this, url_for("static", filename="style.css") prints /static/style.css. In the browser the page loads with your stylesheet, script, and logo all wired up correctly.
3 Styling a Real Template
In a real app the HTML lives in templates/index.html and you render it with render_template. The route stays tiny; the template and the CSS file do the visual work.
The code below renders index.html and passes a page title. The template links static/style.css, and the route returns the finished HTML response.
# app.py — render a template that pulls in a stylesheet
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def home():
# render_template loads templates/index.html and fills in the variables
return render_template("index.html", title="Welcome")
# templates/index.html would contain:
# <link rel="stylesheet"
# href="{{ url_for('static', filename='style.css') }}">
# <h1>{{ title }}</h1>
#
# static/style.css would contain:
# body { font-family: sans-serif; background: #f4f4f4; }
# h1 { color: #3776AB; }
if __name__ == "__main__":
app.run(debug=True)
print("Route registered:", [str(r) for r in app.url_map.iter_rules()])
print("Run 'flask run' and open http://127.0.0.1:5000 to see the styled page.")🔬 Static File Quick Map
| File on disk | URL in the browser |
|---|---|
| static/style.css | /static/style.css |
| static/js/app.js | /static/js/app.js |
| static/img/logo.png | /static/img/logo.png |
| static/fonts/ui.woff2 | /static/fonts/ui.woff2 |
🎯 Your Turn: Fill in the Blanks
Complete the app and template below. Replace each ___ so the page links its stylesheet and image from the static folder.
# 🎯 YOUR TURN: replace each ___ to link static files
from flask import Flask, render_template_string
app = Flask(__name__)
PAGE = """
<!doctype html>
<html>
<head>
<!-- Load static/style.css -->
<link rel="stylesheet" href="{{ url_for('___', filename='___') }}">
</head>
<body>
<!-- Load static/logo.png -->
<img src="{{ url_for('static', filename='___') }}" alt="Logo">
<h1>Fill in the blanks!</h1>
</body>
</html>
"""
@app.route("/")
def home():
return ___(PAGE) # which function renders an HTML string?
print("Defined route:", [str(r) for r in app.url_map.iter_rules()])Common Errors & Quick Reference
❌ My CSS returns a 404 Not Found
The file isn't inside the static folder, or the filename is misspelled. Confirm the path is static/style.css and that you referenced filename='style.css' exactly.
❌ Styles work locally but break after deploy
You hard-coded /static/style.css. Use url_for('static', filename='style.css') so the URL is rebuilt correctly no matter where the app is mounted.
| Task | Code |
|---|---|
| Link a stylesheet | url_for('static', filename='style.css') |
| Link an image | url_for('static', filename='logo.png') |
| Rename the folder | Flask(__name__, static_folder='assets') |
| Run the app | flask run |
🏆 Mini Challenge: Add a Stylesheet and a Script
Build a page that pulls in both a stylesheet and a JavaScript file from the static folder.
- Link static/style.css in the page head with url_for
- Link static/script.js just before the closing body tag
- Render the page from a route at /
# 🏆 Mini Challenge: a page with CSS and JS
from flask import Flask, render_template_string
app = Flask(__name__)
PAGE = """
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<h1>Styled and scripted!</h1>
<script src="{{ url_for('static', filename='script.js') }}"></script>
</body>
</html>
"""
@app.route("/")
def home():
return render_template_string(PAGE)
# Confirm both static URLs build correctly
with app.test_request_context():
from flask import url_for
print("CSS:", url_for("static", filename="style.css"))
print("JS :", url_for("static", filename="script.js"))❓ Frequently Asked Questions
Lesson complete — your pages can have style now!
You learned where static files live, how Flask serves them at /static/, and why url_for('static', filename=...) is the safe way to link them from templates.
Practice quiz
By default, which folder does Flask serve static files from?
- public
- static
- assets
- files
Answer: static. Flask reserves a folder named 'static' next to your app and serves its files automatically.
At what URL is static/style.css served by default?
- /assets/style.css
- /css/style.css
- /files/style.css
- /static/style.css
Answer: /static/style.css. Files in the static folder are reachable at the /static/ URL, so static/style.css maps to /static/style.css.
Which call builds the URL to a static file in a template?
- url_for('static', filename='style.css')
- static_url('style.css')
- url('static/style.css')
- asset('style.css')
Answer: url_for('static', filename='style.css'). url_for('static', filename='...') asks Flask for the correct path to a static asset.
Why prefer url_for over hard-coding /static/style.css?
- It is shorter to type
- It caches files automatically
- It rebuilds the correct URL even if the app moves to a subpath
- It minifies the CSS
Answer: It rebuilds the correct URL even if the app moves to a subpath. Hard-coded paths break when the app is mounted elsewhere; url_for always generates a valid link.
How do you rename the static folder to 'assets'?
- app.static_folder = True
- Flask(__name__, static_folder='assets')
- app.config['STATIC'] = 'assets'
- Flask(static='assets')
Answer: Flask(__name__, static_folder='assets'). Pass static_folder='assets' to Flask() to change the folder name from the default 'static'.
Where must the static folder sit?
- Inside templates/
- Anywhere on the system path
- In the project root only after deploy
- Next to app.py (or your package)
Answer: Next to app.py (or your package). Flask finds the static folder using the __name__ you passed to Flask(__name__), so it must sit next to your app.
What argument names the file inside url_for('static', ...)?
- filename
- path
- src
- file
Answer: filename. You pass filename='style.css' to identify the file relative to the static folder.
A static CSS request returns 404. What is a likely cause?
- The browser cache is stale
- Debug mode is off
- The file is not inside the static folder or is misspelled
- url_for was used
Answer: The file is not inside the static folder or is misspelled. A 404 usually means the file is not in the static folder or the filename does not match exactly.
Which folder holds the HTML that links the static files?
- static/
- views/
- pages/
- templates/
Answer: templates/. Rendered HTML lives in templates/, and those templates link to assets in static/ via url_for.
What does the 'static' argument to url_for refer to?
- A custom view you must define
- The built-in static endpoint
- A config flag
- The templates folder
Answer: The built-in static endpoint. 'static' is the built-in endpoint Flask registers for serving files from the static folder.
Continue this course
- Previous: Templates with Jinja2
- Next: Request & Response Objects — Inspect incoming requests and craft custom responses with headers and status codes
- Quick reference: Flask cheat sheet › Templates (Jinja2)
Frequently asked questions
Where do static files go in a Flask project?
By default Flask serves files from a folder named 'static' next to your app. CSS, JavaScript, and images placed there are available at the /static/ URL automatically — no extra route needed.
Why should I use url_for('static', filename=...) instead of typing the path?
url_for builds the correct URL even if your app moves to a subpath or you change the static folder. Hard-coding '/static/style.css' breaks in those cases, while url_for always generates a valid, cache-friendly link.
Can I rename the static folder?
Yes. Pass static_folder and static_url_path to Flask(), for example Flask(__name__, static_folder='assets'). Most projects keep the default 'static' name because tools and tutorials expect it.
Why isn't my CSS updating in the browser?
Browsers cache static files aggressively. Hard-refresh with Ctrl+Shift+R, or add a version query string. Also confirm the file is inside the static folder and that flask run is serving the latest code.