0
# Static Files and Templating
1
2
FastAPI provides built-in support for serving static files and rendering HTML templates, enabling the creation of full web applications that combine API endpoints with frontend content. This functionality is essential for applications that need to serve HTML pages, CSS, JavaScript, images, and other static assets alongside their API functionality.
3
4
## Capabilities
5
6
### Static Files Serving
7
8
Class for serving static files such as HTML, CSS, JavaScript, images, and other assets from the filesystem.
9
10
```python { .api }
11
class StaticFiles:
12
def __init__(
13
self,
14
*,
15
directory: str = None,
16
packages: List[str] = None,
17
html: bool = False,
18
check_dir: bool = True,
19
follow_symlink: bool = False,
20
) -> None:
21
"""
22
Create static files application for serving static content.
23
24
Parameters:
25
- directory: Directory path containing static files
26
- packages: List of Python packages containing static files
27
- html: Whether to serve HTML files for directory requests
28
- check_dir: Whether to check if directory exists on startup
29
- follow_symlink: Whether to follow symbolic links
30
"""
31
```
32
33
### Jinja2 Template Engine
34
35
Template engine integration for rendering dynamic HTML content with data from your API endpoints.
36
37
```python { .api }
38
class Jinja2Templates:
39
def __init__(self, directory: str) -> None:
40
"""
41
Create Jinja2 templates instance.
42
43
Parameters:
44
- directory: Directory containing template files
45
"""
46
47
def TemplateResponse(
48
self,
49
name: str,
50
context: dict,
51
status_code: int = 200,
52
headers: dict = None,
53
media_type: str = None,
54
background: BackgroundTask = None,
55
) -> TemplateResponse:
56
"""
57
Render template with context data and return as HTTP response.
58
59
Parameters:
60
- name: Template file name
61
- context: Dictionary of template variables
62
- status_code: HTTP status code
63
- headers: Additional HTTP headers
64
- media_type: Response content type
65
- background: Background task to run after response
66
67
Returns:
68
TemplateResponse with rendered HTML content
69
"""
70
71
def get_template(self, name: str) -> Template:
72
"""Get template object by name."""
73
```
74
75
## Usage Examples
76
77
### Serving Static Files
78
79
```python
80
from fastapi import FastAPI
81
from fastapi.staticfiles import StaticFiles
82
83
app = FastAPI()
84
85
# Mount static files directory
86
app.mount("/static", StaticFiles(directory="static"), name="static")
87
88
# Mount static files with HTML serving
89
app.mount("/public", StaticFiles(directory="public", html=True), name="public")
90
91
# Serve from Python package
92
app.mount("/assets", StaticFiles(packages=["mypackage"]), name="assets")
93
```
94
95
With this setup:
96
- Files in `./static/` are accessible at `/static/filename.ext`
97
- Files in `./public/` are accessible at `/public/filename.ext`
98
- Directory requests to `/public/` will serve `index.html` if `html=True`
99
- Package assets are served from installed Python packages
100
101
### HTML Template Rendering
102
103
```python
104
from fastapi import FastAPI, Request
105
from fastapi.templating import Jinja2Templates
106
from fastapi.responses import HTMLResponse
107
108
app = FastAPI()
109
templates = Jinja2Templates(directory="templates")
110
111
@app.get("/", response_class=HTMLResponse)
112
async def home(request: Request):
113
return templates.TemplateResponse(
114
"index.html",
115
{"request": request, "title": "Home Page"}
116
)
117
118
@app.get("/user/{user_id}", response_class=HTMLResponse)
119
async def user_profile(request: Request, user_id: int):
120
# Simulate user data retrieval
121
user_data = {"id": user_id, "name": f"User {user_id}"}
122
return templates.TemplateResponse(
123
"profile.html",
124
{
125
"request": request,
126
"user": user_data,
127
"title": f"Profile - {user_data['name']}"
128
}
129
)
130
```
131
132
Template file `templates/index.html`:
133
134
```html
135
<!DOCTYPE html>
136
<html>
137
<head>
138
<title>{{ title }}</title>
139
<link rel="stylesheet" href="/static/style.css">
140
</head>
141
<body>
142
<h1>Welcome to FastAPI</h1>
143
<p>This is a template-rendered page.</p>
144
<script src="/static/script.js"></script>
145
</body>
146
</html>
147
```
148
149
Template file `templates/profile.html`:
150
151
```html
152
<!DOCTYPE html>
153
<html>
154
<head>
155
<title>{{ title }}</title>
156
</head>
157
<body>
158
<h1>User Profile</h1>
159
<p>ID: {{ user.id }}</p>
160
<p>Name: {{ user.name }}</p>
161
<a href="/">Back to Home</a>
162
</body>
163
</html>
164
```
165
166
### Complete Web Application Example
167
168
```python
169
from fastapi import FastAPI, Request, Form
170
from fastapi.templating import Jinja2Templates
171
from fastapi.staticfiles import StaticFiles
172
from fastapi.responses import HTMLResponse, RedirectResponse
173
from typing import Optional
174
175
app = FastAPI()
176
177
# Mount static files for CSS, JS, images
178
app.mount("/static", StaticFiles(directory="static"), name="static")
179
180
# Setup templates
181
templates = Jinja2Templates(directory="templates")
182
183
# In-memory storage for demo
184
items = []
185
186
@app.get("/", response_class=HTMLResponse)
187
async def home(request: Request):
188
return templates.TemplateResponse(
189
"home.html",
190
{"request": request, "items": items}
191
)
192
193
@app.get("/add", response_class=HTMLResponse)
194
async def add_item_form(request: Request):
195
return templates.TemplateResponse(
196
"add_item.html",
197
{"request": request}
198
)
199
200
@app.post("/add")
201
async def add_item(
202
request: Request,
203
name: str = Form(...),
204
description: str = Form(...)
205
):
206
item = {"id": len(items) + 1, "name": name, "description": description}
207
items.append(item)
208
return RedirectResponse(url="/", status_code=303)
209
210
@app.get("/item/{item_id}", response_class=HTMLResponse)
211
async def item_detail(request: Request, item_id: int):
212
item = next((item for item in items if item["id"] == item_id), None)
213
if not item:
214
return templates.TemplateResponse(
215
"404.html",
216
{"request": request},
217
status_code=404
218
)
219
return templates.TemplateResponse(
220
"item_detail.html",
221
{"request": request, "item": item}
222
)
223
224
# API endpoints for AJAX/SPA integration
225
@app.get("/api/items")
226
async def api_get_items():
227
return {"items": items}
228
229
@app.post("/api/items")
230
async def api_add_item(item: dict):
231
new_item = {"id": len(items) + 1, **item}
232
items.append(new_item)
233
return new_item
234
```
235
236
### Template with Custom Filters
237
238
```python
239
from fastapi import FastAPI, Request
240
from fastapi.templating import Jinja2Templates
241
import datetime
242
243
app = FastAPI()
244
templates = Jinja2Templates(directory="templates")
245
246
# Add custom filter to templates
247
def format_datetime(value):
248
return value.strftime("%Y-%m-%d %H:%M:%S")
249
250
templates.env.filters["datetime"] = format_datetime
251
252
@app.get("/dashboard", response_class=HTMLResponse)
253
async def dashboard(request: Request):
254
return templates.TemplateResponse(
255
"dashboard.html",
256
{
257
"request": request,
258
"current_time": datetime.datetime.now(),
259
"data": {"users": 150, "posts": 1240}
260
}
261
)
262
```
263
264
Template using custom filter:
265
266
```html
267
<!DOCTYPE html>
268
<html>
269
<head>
270
<title>Dashboard</title>
271
</head>
272
<body>
273
<h1>Dashboard</h1>
274
<p>Current time: {{ current_time | datetime }}</p>
275
<p>Users: {{ data.users }}</p>
276
<p>Posts: {{ data.posts }}</p>
277
</body>
278
</html>
279
```
280
281
### Error Page Templates
282
283
```python
284
from fastapi import FastAPI, Request, HTTPException
285
from fastapi.templating import Jinja2Templates
286
from fastapi.responses import HTMLResponse
287
288
app = FastAPI()
289
templates = Jinja2Templates(directory="templates")
290
291
@app.exception_handler(404)
292
async def not_found_handler(request: Request, exc: HTTPException):
293
return templates.TemplateResponse(
294
"404.html",
295
{"request": request},
296
status_code=404
297
)
298
299
@app.exception_handler(500)
300
async def server_error_handler(request: Request, exc: HTTPException):
301
return templates.TemplateResponse(
302
"500.html",
303
{"request": request},
304
status_code=500
305
)
306
```
307
308
## Types
309
310
```python { .api }
311
from typing import Any, Dict, List, Optional
312
from starlette.responses import Response
313
from starlette.background import BackgroundTask
314
from starlette.templating import _TemplateResponse as TemplateResponse
315
from jinja2 import Template, Environment
316
317
# Template response type
318
TemplateResponse = _TemplateResponse
319
320
# Jinja2 environment type
321
Jinja2Environment = Environment
322
323
# Template context type
324
TemplateContext = Dict[str, Any]
325
```