How to Render HTML with FastAPI and Jinja2: A Step‑by‑Step Guide
This tutorial walks you through installing FastAPI, setting up a project structure, creating routes that serve Jinja2‑rendered HTML pages, adding static files, building forms, and mastering essential Jinja2 syntax such as variables, conditionals, loops, template inheritance, filters, macros, and security considerations.
FastAPI can render HTML pages, making it an excellent choice for full‑stack applications that need server‑side HTML rendering.
Install required packages
<code>pip install fastapi uvicorn jinja2</code>Project structure should look like:
<code>fastapi/
├── main.py
├── templates/
│ └── index.html
└── static/
└── style.css</code>Create the application
<code># main.py
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
app = FastAPI()
# Serve static files
app.mount("/static", StaticFiles(directory="static"), name="static")
# Set template directory
templates = Jinja2Templates(directory="templates")
@app.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
return templates.TemplateResponse("index.html", {"request": request, "message": "Hello from FastAPI!"})
</code>index.html
<code><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>FastAPI HTML</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<h1>{{ message }}</h1>
<p>This HTML page is rendered by FastAPI using Jinja2.</p>
</body>
</html>
</code>You can also add CSS styles in the style.css file.
Next, add a new route /form that renders an HTML template containing a simple login form.
Add the following to main.py
<code>from fastapi import Form
@app.get("/form", response_class=HTMLResponse)
async def form_get(request: Request):
return templates.TemplateResponse("form.html", {"request": request})
@app.post("/form", response_class=HTMLResponse)
async def form_post(request: Request, name: str = Form(...)):
return templates.TemplateResponse("form.html", {"request": request, "name": name})
</code>form.html should look like:
<code><!DOCTYPE html>
<html>
<head>
<title>Form Example</title>
</head>
<body>
<form method="post">
<input type="text" name="name" placeholder="Enter your name" required>
<button type="submit">Submit</button>
</form>
{% if name %}
<p>Hello, {{ name }}!</p>
{% endif %}
</body>
</html>
</code>Through the above, you have learned how to render HTML pages and use Jinja2 tags.
Common Jinja2 tag syntax
1. Variable interpolation
<code><h1>Hello, {{ username }}!</h1></code>2. Conditional statements
<code>{% if username %}
<p>Welcome, {{ username }}!</p>
{% else %}
<p>Please log in.</p>
{% endif %}</code>3. Loops
<code><ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul></code>4. Template inheritance
base.html:
<code><!DOCTYPE html>
<html>
<head>
<title>{% block title %}My Site{% endblock %}</title>
</head>
<body>
<header>Header Here</header>
<main>{% block content %}{% endblock %}</main>
<footer>Footer Here</footer>
</body>
</html>
</code>index.html:
<code>{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block content %}
<h1>Welcome to Home Page</h1>
{% endblock %}
</code>5. Comments
<code>{# This is a comment and will not appear in the rendered HTML #}</code>6. Filters
<code><p>{{ username | upper }}</p> <!-- outputs uppercase -->
<p>{{ user.age | default(18) }}</p> <!-- shows 18 if age is missing -->
</code>7. Macros (reusable snippets)
Define a macro:
<code>{% macro input(name, value='', type='text') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
</code>Use the macro:
<code>{% from "macros.html" import input %}
<form>
{{ input('username') }}
</form>
</code>Jinja2 and security
Jinja2 automatically escapes HTML to prevent XSS attacks:
<code>name = "<script>alert(1)</script>"
{{ name }}
</code>The output will be:
<code>&lt;script&gt;alert(1)&lt;/script&gt;</code>If you need to render raw HTML (only for trusted content), use the |safe filter:
<code>{{ html_snippet | safe }}</code>Conclusion
By following this guide, you now understand the basic method of combining FastAPI with Jinja2 to render HTML pages, as well as the common Jinja2 syntax and features that can help you build dynamic web applications.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.