Jinja2 Fragments allows rendering individual blocks from Jinja2 templates. This library was created to enable the pattern of Template Fragments with Jinja2. It's a great pattern if you are using HTMX or some other library that leverages fetching partial HTML.
With jinja2, if you have a template block that you want to render by itself and as part of another page, you are forced to put that block on a separate file and then use the include tag (or Jinja Partials) on the wrapping template.
With Jinja2 Fragments, following the Locality of Behavior design principle, you have a single file for both cases. See below for examples.
The detailed documentation is available at Read the Docs. The rest of this README is a summarized version of it.
If you discover a security vulnerability, please report it privately following the instructions in SECURITY.md.
It's just pip install jinja2-fragments and you're all set. It's a pure Python package
that only needs jinja2 (for obvious reasons!).
This is an example of how to use the library with vanilla Jinja2. Given the template page.html.jinja2:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>This is the title</title>
</head>
<body>
<h1>This is a header</h1>
{% block content %}
<p>This is the magic number: {{ magic_number }}.</p>
{% endblock %}
</body>
</html>If you want to render only the content block, do:
from jinja2 import Environment, FileSystemLoader, select_autoescape
from jinja2_fragments import render_block
environment = Environment(
loader=FileSystemLoader("my_templates"),
autoescape=select_autoescape(("html", "jinja2")),
)
rendered_html = render_block(
environment, "page.html.jinja2", "content", magic_number=42
)And this will only render:
<p>This is the magic number: 42.</p>With the variant render_blocks (notice the plural) it is also possible to render
multiple blocks from the same template and concatenate them all to return them in a
single response. This enables easier
out-of-band updates when using HTMX.
If you want to use Jinja2 Fragments with Flask, assuming the same template as the example above, do:
from flask import Flask, render_template
from jinja2_fragments.flask import render_block
app = Flask(__name__)
@app.get("/full_page")
def full_page():
return render_template("page.html.jinja2", magic_number=42)
@app.get("/only_content")
def only_content():
return render_block("page.html.jinja2", "content", magic_number=42)If you want to use Jinja2 Fragments with Quart, assuming the same template as the example above, do:
from quart import Quart, render_template
from jinja2_fragments.quart import render_block
app = Quart(__name__)
@app.get("/full_page")
async def full_page():
return await render_template("page.html.jinja2", magic_number=42)
@app.get("/only_content")
async def only_content():
return await render_block("page.html.jinja2", "content", magic_number=42)You can use Jinja2 Fragments with Starlette through the Jinja2Blocks class, which extends Starlette's Jinja2Templates.
Assuming the same template as the examples above:
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.routing import Route
from jinja2_fragments.starlette import Jinja2Blocks
templates = Jinja2Blocks(directory="path/to/templates")
async def full_page(request: Request):
return templates.TemplateResponse(request, "page.html.jinja2", {"magic_number": 42})
async def only_content(request: Request):
return templates.TemplateResponse(
request, "page.html.jinja2", {"magic_number": 42}, block_name="content"
)
routes = [
Route("/full_page", full_page),
Route("/only_content", only_content),
]
app = Starlette(routes=routes)You can also use Jinja2 Fragments with FastAPI. In this case, Jinja2 Fragments has a wrapper around the FastAPI Jinja2Templates object called Jinja2Blocks.
It functions exactly the same, but allows you to include an optional parameter to the TemplateResponse that includes the block_name you want to render.
Assuming the same template as the examples above:
from fastapi import FastAPI
from fastapi.requests import Request
from jinja2_fragments.fastapi import Jinja2Blocks
app = FastAPI()
templates = Jinja2Blocks(directory="path/to/templates")
@app.get("/full_page")
async def full_page(request: Request):
return templates.TemplateResponse(request, "page.html.jinja2", {"magic_number": 42})
@app.get("/only_content")
async def only_content(request: Request):
return templates.TemplateResponse(
request, "page.html.jinja2", {"magic_number": 42}, block_name="content"
)You can use jinja2-fragments's render() with Sanic as a drop-in replacement of the Sanic template extension's render(). Your request context and environment configuration will work the same as before. You must have sanic_ext and Jinja2 installed.
By default, the full page is rendered (block=None) unless you provide a block keyword argument.
from sanic import Sanic, Request
import sanic_ext
from jinja2_fragments.sanic import render
app = Sanic(__name__)
app.extend(config=sanic_ext.Config(templating_path_to_templates="path/to/templates"))
@app.get("/full_page")
async def full_page(request: Request):
return await render("page.html.jinja2", context={"magic_number": 42})
@app.get("/only_content")
async def only_content(request: Request):
return await render(
"page.html.jinja2", block="content", context={"magic_number": 42}
)You can use Jinja2 Fragments with Litestar by using the HTMXBlockTemplate class. This gives you access to the block_name parameter when rendering the template.
By default, the full page is rendered unless you provide a block_name keyword argument.
Note:
HTMXBlockTemplatecan be used as a drop-in replacement for Litestar'sTemplateclass. However, passing multiple positional arguments toHTMXBlockTemplateis deprecated and will be removed in a future version. Usetemplate_nameas the only positional argument and pass all other parameters as keyword arguments.
try:
# litestar>=2.13.0
from litestar.plugins.htmx import HTMXRequest
except ImportError:
# litestar<2.13.0
from litestar.contrib.htmx.request import HTMXRequest
from litestar import get, Litestar
from litestar.response import Template
from litestar.contrib.jinja import JinjaTemplateEngine
from litestar.template.config import TemplateConfig
from jinja2_fragments.litestar import HTMXBlockTemplate
@get("/full_page")
def full_page(request: HTMXRequest) -> Template:
return HTMXBlockTemplate(
template_name="page.html.jinja2", context={"magic_number": 42}
)
@get("/only_content")
def only_content(request: HTMXRequest) -> Template:
return HTMXBlockTemplate(
template_name="page.html.jinja2",
block_name="content",
context={"magic_number": 42},
)
app = Litestar(
route_handlers=[full_page, only_content],
request_class=HTMXRequest,
template_config=TemplateConfig(
directory="path/to/templates",
engine=JinjaTemplateEngine,
),
)This project uses pre-commit hooks to run Ruff and format Python code examples in documentation with blacken-docs on each commit.
To have that running automatically on your environment, install the project with:
pip install -e .[dev]And then run once:
pre-commit installFrom now on, every time you commit your files on this project, they will be automatically processed by Ruff and blacken-docs.
You can install pytest and other required dependencies with:
pip install -e .[tests]And then run the test suite with:
pytest