Templates
For HTML-heavy endpoints (server-rendered pages, email bodies, report pages), FastAPI integrates Jinja2 templates through the Jinja2Templates class. You pass a templates directory, render a template with context variables, and return the TemplateResponse - all while keeping the rest of the API JSON-based.
Setup
pip install jinja2
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/hello/{name}")
def hello(request: Request, name: str):
return templates.TemplateResponse("hello.html", {"request": request, "name": name})
Recent Starlette versions still expect the request object in the context for URL generation; pass it explicitly as shown.
The Template File
<!doctype html>
<html>
<head><title>Hello</title></head>
<body>
<h1>Hello, {{ name }}!</h1>
<a href="/">Home</a>
</body>
</html>
Template Features
- Variables: {{ name }} with autoescaping on by default in Jinja2.
- Control flow: {% for item in items %} ... {% endfor %}, {% if %}.
- Inheritance: {% extends "base.html" %} with {% block content %} for shared layouts.
- Filters: {{ name | upper }}, {{ price | round(2) }}.
<ul>
{% for item in items %}
<li>{{ item.name }} - ${{ item.price }}</li>
{% endfor %}
</ul>
Generating URLs in Templates
Use the url_for helper with the request object to build URLs that stay correct if the app is mounted at a subpath:
<a href="{{ url_for('hello', name='Ada') }}">Greet Ada</a>
Summary
Render HTML with Jinja2Templates: pass a templates directory, return TemplateResponse("page.html", context) with the request in the context, and use Jinja2 syntax for variables, loops, and base-template inheritance. Templates coexist happily with JSON endpoints in the same app.