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

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.png

With 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.png

When 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 diskURL 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.

TaskCode
Link a stylesheeturl_for('static', filename='style.css')
Link an imageurl_for('static', filename='logo.png')
Rename the folderFlask(__name__, static_folder='assets')
Run the appflask 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.

# 🏆 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

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.