Skip to main content

Jinja Partials

Simple reuse of partial HTML page templates in the Jinja template language for Python web frameworks. (There is also a Pyramid/Chameleon version here.)

Overview

When building real-world web apps with Jinja2, it's easy to end up with repeated HTML fragments. Just like organizing code for reuse, it would be ideal to reuse smaller sections of HTML template code. That's what this library is all about.

Supported Frameworks

Jinja Partials has specific support for the most popular Python web frameworks:

  • Flask - register_extensions(app)
  • FastAPI - register_fastapi_extensions(app, templates)
  • Starlette - register_starlette_extensions(templates, app=app)
  • Quart - register_quart_extensions(app)
  • Any Jinja2 environment - register_environment(env)

Examples

This project includes example applications for each supported framework in the examples folder:

Each example demonstrates the same UI using reusable partial templates:

Installation

It's just pip install jinja-partials and you're all set with this pure Python package.

Usage

Using the library is easy. Register jinja_partials with your framework once at app startup.

Flask

import flask
import jinja_partials

app = flask.Flask(__name__)
jinja_partials.register_extensions(app)

FastAPI

from fastapi import FastAPI
from fastapi.templating import Jinja2Templates
import jinja_partials

app = FastAPI()
templates = Jinja2Templates(directory="templates")

jinja_partials.register_fastapi_extensions(app, templates)

Starlette

from starlette.applications import Starlette
from starlette.templating import Jinja2Templates
import jinja_partials

templates = Jinja2Templates(directory="templates")
app = Starlette(routes=[...])

jinja_partials.register_starlette_extensions(templates, app=app)

Quart

from quart import Quart
import jinja_partials

app = Quart(__name__)
jinja_partials.register_quart_extensions(app)

Direct Jinja2 Environment

For any other use case, register directly with a Jinja2 environment:

from jinja2 import Environment, FileSystemLoader
import jinja_partials

environment = Environment(loader=FileSystemLoader("templates"))
jinja_partials.register_environment(environment, markup=True)

Using the Jinja2 Extension (Declarative)

Alternatively, you can use the PartialsJinjaExtension for a more declarative approach.

For Flask Applications

With Flask, add the extension to your app's Jinja environment:

import flask
import jinja_partials

app = flask.Flask(__name__)

# Declarative registration with Flask
app.jinja_env.add_extension('jinja_partials.PartialsJinjaExtension')

# Alternative: traditional registration
# jinja_partials.register_extensions(app)

For Standalone Jinja2 Environments

For direct Jinja2 usage (without Flask):

from jinja2 import Environment, FileSystemLoader

# Declarative registration - extension automatically registers render_partial
environment = Environment(
    loader=FileSystemLoader("tests/test_templates"),
    extensions=["jinja_partials.PartialsJinjaExtension"]
)
# render_partial is now available in templates, no additional setup needed!

When to Use the Extension Approach

This declarative approach is especially useful when:

  • Working with frameworks that configure Jinja2 environments declaratively
  • You want cleaner, more explicit dependency management
  • Integrating with other Jinja2 extensions
  • Using standalone Jinja2 environments outside of Flask

The extension automatically enables markup support by default, ensuring that your partial templates return properly escaped Markup objects.

Template Usage

Next, you define your main HTML (Jinja2) templates as usual. Then define your partial templates. I recommend locating and naming them accordingly:

├── templates
│   ├── home
│   │   ├── index.html
│   │   └── listing.html
│   └── shared
│       ├── _layout.html
│       └── partials
│           ├── video_image.html
│           └── video_square.html

Notice the partials subfolder in the templates/shared folder.

The templates are just HTML fragments. Here is a stand-alone one for the YouTube thumbnail from the example app:

<img src="https://img.youtube.com/vi/{{ video.id }}/maxresdefault.jpg"
     class="img img-responsive {{ ' '.join(classes) }}"
     alt="{{ video.title }}"
     title="{{ video.title }}">

Notice that an object called video and list of classes are passed in as the model.

Templates can also be nested. Here is the whole single video fragment with the image as well as other info linking out to YouTube:

<div>
    <a href="https://www.youtube.com/watch?v={{ video.id }}" target="_blank">
        {{ render_partial('shared/partials/video_image.html', video=video) }}
    </a>
    <a href="https://www.youtube.com/watch?v={{ video.id }}" target="_blank"
       class="author">{{ video.author }}</a>
    <div class="views">{{ "{:,}".format(video.views) }} views</div>
</div>

Now you see the render_partial() method. It takes the subpath into the templates folder and any model data passed in as keyword arguments.

We can finally generate the list of video blocks as follows:

{% for v in videos %}

    <div class="col-md-3 video">
        {{ render_partial('shared/partials/video_square.html', video=v) }}
    </div>

{% endfor %}

This time, we reframe each item in the list from the outer template (called v) as the video model in the inner HTML section.

Why not just use include or macro from Jinja?

The short answer is they are nearly the same, but both fall short in different ways. For a more detailed response, see the discussion on issue #1

Metadata

Release files for jinja_partials 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jinja_partials 0.3.1
File Size Uploaded Provenance Actions
jinja_partials-0.3.1.tar.gz 3.0 MB Without provenance Details for jinja_partials-0.3.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for jinja_partials 0.3.1
File Interpreter ABI Platform Actions
jinja_partials-0.3.1-py3-none-any.whl Python 3 none any Details for jinja_partials-0.3.1-py3-none-any.whl

Total release size: 3.0 MB

Release files / jinja_partials-0.3.1.tar.gz

Download URL jinja_partials-0.3.1.tar.gz
Size 3.0 MB
Tags Source
SHA-256 checksum
How to use checksums
0c53d6e6489617a7efd403506040d5be7a138b95b7051e727cd8f42b0d8159a2
BLAKE2b-256 checksum
How to use checksums
466bb8728a82d84bf6bd3a34b64e249568f41da21225489c9e1c244befa153ca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / jinja_partials-0.3.1-py3-none-any.whl

Download URL jinja_partials-0.3.1-py3-none-any.whl
Size 7.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f8569e70fd98d5d2b1b5bc9f3acfece1e75330d7acd5cd6d7154009f7c2d4619
BLAKE2b-256 checksum
How to use checksums
5229cd2a7241e67733f8352cc20d920cdd6bd5a7b3ebf6c0f82b57b13416ace8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for jinja_partials 0.3.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page