Skip to main content

Render Jinja2 template block as HTML page fragments on Python web frameworks.

Project description

Jinja2 fragments

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.

Documentation

The detailed documentation is available at Read the Docs. The rest of this README is a summarized version of it.

Install

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!).

Usage

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>

Rendering multiple blocks

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.

Usage with Flask

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)

Usage with Quart

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)

Usage with Starlette

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)

Usage with FastAPI

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"
    )

Usage with Sanic

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}
    )

Usage with Litestar

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: HTMXBlockTemplate can be used as a drop-in replacement for Litestar's Template class. However, passing multiple positional arguments to HTMXBlockTemplate is deprecated and will be removed in a future version. Use template_name as 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,
    )
)

How to collaborate

This project uses pre-commit hooks to run Ruff 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 install

From now on, every time you commit your files on this project, they will be automatically processed by Ruff.

How to run tests

You can install pytest and other required dependencies with:

pip install -e .[tests]

And then run the test suite with:

pytest

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jinja2_fragments-1.11.0.tar.gz (21.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jinja2_fragments-1.11.0-py3-none-any.whl (16.0 kB view details)

Uploaded Python 3

File details

Details for the file jinja2_fragments-1.11.0.tar.gz.

File metadata

  • Download URL: jinja2_fragments-1.11.0.tar.gz
  • Upload date:
  • Size: 21.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for jinja2_fragments-1.11.0.tar.gz
Algorithm Hash digest
SHA256 240eabb7faaa379110cf8e43acb81fb8731fd6ae39c7a1ae232e4421c4804248
MD5 2a959052f8219e76237f68c303fa8d7f
BLAKE2b-256 d00651681ecdfe06a51c458da481f353bfc9325d56491fec2be138b63e93e2bb

See more details on using hashes here.

File details

Details for the file jinja2_fragments-1.11.0-py3-none-any.whl.

File metadata

File hashes

Hashes for jinja2_fragments-1.11.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3b37105d565b96129e2e34df040d1b7bb71c8a76014f7b5e1aa914ccf3f9256c
MD5 904995261681ef5d7912acce7520d0f2
BLAKE2b-256 7b4db65f80e4aca3a630105f48192dac6ed16699e6d53197899840da2d67c3a5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page