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 jinja2

Project Layout

myapp/
├── main.py
├── static/
│   ├── style.css
│   └── logo.png
└── templates/
    └── home.html

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

Files 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  ──▶  Browser

Template 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_for to link static files reliably.
  • Template inheritance keeps layouts consistent.
  • Jinja2 escapes user text automatically.