Skip to content

Templates

Starlette is not strictly coupled to any particular templating engine, but Jinja2 provides an excellent choice.

API Reference

starlette.templating.Jinja2Templates

Jinja2Templates(
    directory: (
        str | PathLike[str] | Sequence[str | PathLike[str]]
    ),
    *,
    context_processors: (
        list[Callable[[Request], dict[str, Any]]] | None
    ) = None
)
Jinja2Templates(
    *,
    env: Environment,
    context_processors: (
        list[Callable[[Request], dict[str, Any]]] | None
    ) = None
)
Jinja2Templates(
    directory: (
        str
        | PathLike[str]
        | Sequence[str | PathLike[str]]
        | None
    ) = None,
    *,
    context_processors: (
        list[Callable[[Request], dict[str, Any]]] | None
    ) = None,
    env: Environment | None = None
)

Jinja2 template renderer.

Example
from starlette.templating import Jinja2Templates

templates = Jinja2Templates(directory="templates")

async def homepage(request: Request) -> Response:
    return templates.TemplateResponse(request, "index.html")

Starlette provides a simple way to get jinja2 configured. This is probably what you want to use by default.

from starlette.applications import Starlette
from starlette.routing import Route, Mount
from starlette.templating import Jinja2Templates
from starlette.staticfiles import StaticFiles


templates = Jinja2Templates(directory='templates')

async def homepage(request):
    return templates.TemplateResponse(request, 'index.html')

routes = [
    Route('/', endpoint=homepage),
    Mount('/static', StaticFiles(directory='static'), name='static')
]

app = Starlette(debug=True, routes=routes)

Note that the incoming request instance must be included as part of the template context.

The Jinja2 template context will automatically include a url_for function, so we can correctly hyperlink to other pages within the application.

For example, we can link to static files from within our HTML templates:

<link href="{{ url_for('static', path='/css/bootstrap.min.css') }}" rel="stylesheet" />

If you want to use custom filters, you will need to update the env property of Jinja2Templates:

from commonmark import commonmark
from starlette.templating import Jinja2Templates

def marked_filter(text):
    return commonmark(text)

templates = Jinja2Templates(directory='templates')
templates.env.filters['marked'] = marked_filter

Using custom jinja2.Environment instance

Starlette also accepts a preconfigured jinja2.Environment instance.

import jinja2
from starlette.templating import Jinja2Templates

env = jinja2.Environment(...)
templates = Jinja2Templates(env=env)

Autoescape

When using the directory argument, Starlette enables autoescape by default for .html, .htm, and .xml templates using jinja2.select_autoescape().

This protects against Cross-Site Scripting (XSS) vulnerabilities by escaping user-provided content before rendering it in the template. For example, if a user submits <script>alert('XSS')</script> as their name, it will be rendered as &lt;script&gt;alert('XSS')&lt;/script&gt; instead of being executed as JavaScript.

Context processors

A context processor is a function that returns a dictionary to be merged into a template context. Every function takes only one argument request and must return a dictionary to add to the context.

A common use case of template processors is to extend the template context with shared variables.

import typing
from starlette.requests import Request

def app_context(request: Request) -> typing.Dict[str, typing.Any]:
    return {'app': request.app}

Registering context templates

Pass context processors to context_processors argument of the Jinja2Templates class.

import typing

from starlette.requests import Request
from starlette.templating import Jinja2Templates

def app_context(request: Request) -> typing.Dict[str, typing.Any]:
    return {'app': request.app}

templates = Jinja2Templates(
    directory='templates', context_processors=[app_context]
)

Info

Asynchronous functions as context processors are not supported.

Testing template responses

When using the test client, template responses include .template and .context attributes.

from starlette.testclient import TestClient


def test_homepage():
    client = TestClient(app)
    response = client.get("/")
    assert response.status_code == 200
    assert response.template.name == 'index.html'
    assert "request" in response.context

Asynchronous template rendering

Jinja2 supports async template rendering, however as a general rule we'd recommend that you keep your templates free from logic that invokes database lookups, or other I/O operations.

Instead we'd recommend that you ensure that your endpoints perform all I/O, for example, strictly evaluate any database queries within the view and include the final results in the context.