FastAPI Static Files and Templates
FastAPI shines as an API, and it can serve complete web pages. Two tools make this possible. Static files deliver fixed assets such as images, stylesheets, and scripts. Templates build HTML pages that change with your data. Think of a printed menu and a daily specials board: the menu never changes, while the board shows today's items.
Installing Jinja2
Templates rely on the Jinja2 engine.
pip install jinja2Project Layout
myapp/
├── main.py
├── static/
│ ├── style.css
│ └── logo.png
└── templates/
└── home.htmlThe static folder holds files that never change. The templates folder holds HTML files with placeholders.
Mounting Static Files
Mounting attaches a folder to a URL path.
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
File on disk URL in the browser
────────────────────── ──────────────────────────────
static/style.css ──▶ /static/style.css
static/logo.png ──▶ /static/logo.pngFiles placed in the folder become available without extra routes.
Setting Up Templates
from fastapi import Request
from fastapi.templating import Jinja2Templates
templates = Jinja2Templates(directory="templates")
@app.get("/")
def home(request: Request):
return templates.TemplateResponse(
request, "home.html", {"title": "Welcome", "items": ["Tea", "Coffee", "Juice"]}
)The route passes the request, the template name, and a dictionary of values. Each key in the dictionary becomes a variable inside the template.
Writing a Template
Double curly braces print a value. Percent braces run logic such as loops and conditions.
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<link rel="stylesheet" href="{{ url_for('static', path='style.css') }}">
</head>
<body>
<h1>{{ title }}</h1>
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
</body>
</html>The url_for helper builds the correct address for a static file, even when the app moves to a new path.
How a Page Gets Built
Browser asks for /
│
▼
Route function ──▶ data: title, items
│
▼
Jinja2 merges data into home.html
│
▼
Finished HTML page ──▶ BrowserTemplate Syntax at a Glance
{{ name }}prints a variable.{% if user %}runs content when a condition holds.{% for x in list %}repeats content for each item.{# note #}adds a comment that never reaches the browser.
Reusing Layouts
Template inheritance avoids copying the header and footer onto every page. A base file defines blocks, and child files fill them.
<!-- base.html -->
<body>
<nav>My Site</nav>
{% block content %}{% endblock %}
</body>
<!-- about.html -->
{% extends "base.html" %}
{% block content %}<h1>About Us</h1>{% endblock %}Safety Built In
Jinja2 escapes special characters by default. A visitor who types a script tag into a form sees plain text on the page rather than running code. Keep this default on and avoid the |safe filter for user content.
Common Mistakes
Missing Request Object
Every template route needs request: Request. Without it, helpers such as url_for fail.
Wrong Folder Name
The directory passed to StaticFiles must exist when the app starts. A typo raises an error at launch.
When to Choose Templates
Templates suit pages that search engines should read, such as marketing pages, documentation, and simple dashboards. The server delivers finished HTML, so visitors see content immediately and crawlers index the text without running scripts.
Large interactive applications often pair FastAPI with a separate frontend framework. In that design, FastAPI serves JSON and the frontend builds the screens. Both styles work well. Many teams use templates for a handful of public pages and JSON routes for everything else, all inside one FastAPI project.
Keep static assets small. Compress images, combine stylesheets where sensible, and let a reverse proxy such as Nginx serve heavy static traffic in production.
Key Points
- Static files serve unchanging assets through
app.mount. - Templates merge data into HTML with Jinja2.
- Use
url_forto link static files reliably. - Template inheritance keeps layouts consistent.
- Jinja2 escapes user text automatically.
