SmarterReactTemplateTagManager

Vite-generated React manifest.json loader and asset collector base class for Django templatetags.

This module provides a reusable base class and supporting types for managing Vite-generated React manifest.json files and collecting frontend assets for React apps in Django projects. It is designed to be instantiated as a singleton representing a specific React app, enabling Django templates to include the correctly-sequenced JavaScript and CSS assets built by Vite for each app.

Key Features

  • Loads and caches the manifest.json for each React app from the static files directory, validated against the Pydantic model ViteManifest.

  • Recursively collects CSS and JS dependencies for the entry point, including all imports.

  • Returns the JS and CSS assets for the entry point as a ReactAppAssets, for use in Django template tags.

  • Designed as a base class to be instantiated for each React app; singletons are used to register template tags per app.

Main Classes and Functions

  • SmarterReactTemplateTagManager: Base class for managing Vite-generated React manifest loading and asset collection for a React app.

  • reactapp_build_assets: The JS and CSS assets for the configured entry point.

  • smarter.lib.django.templatetags.smarter_react_contract: the Pydantic models of the manifest and of the assets.

Usage Example

In a Django template, use the registered template tag for your app to get asset paths:

{% load react_dashboard %}
{% dashboard_react_assets as assets %}
{% for css_file in assets.css %}
    <link class="smarter" rel="stylesheet" href="{% static 'react/smarter-dashboard/' %}{{ css_file }}">
{% endfor %}

Example React manifest.json

A typical React manifest.json looks like this:

{
    "_rolldown-runtime.js": {
        "file": "assets/rolldown-runtime.js",
        "name": "rolldown-runtime"
    },
    "_xterm-TdnZ7DQy.css": {
        "file": "assets/xterm-TdnZ7DQy.css",
        "src": "_xterm-TdnZ7DQy.css"
    },
    "_xterm.js": {
        "file": "assets/xterm.js",
        "name": "xterm",
        "imports": [
            "_rolldown-runtime.js"
        ],
        "css": [
            "assets/xterm-TdnZ7DQy.css"
        ]
    },
    "index.html": {
        "file": "assets/index.js",
        "name": "index",
        "src": "index.html",
        "isEntry": true,
        "imports": [
            "_rolldown-runtime.js",
            "_xterm.js"
        ],
        "css": [
            "assets/index-DvLY75bJ.css"
        ]
    }
}
class smarter.lib.django.templatetags.smarter_react_templatetag_manager.SmarterReactTemplateTagManager(app_name, templatetag_name)[source]

Bases: SmarterHelperMixin

Base class for per-React-app singleton managers that load and analyze Vite-generated React manifest.json files in order to generate ordered lists of JS and CSS assets.

This class is intended to be instantiated once per React app, providing a long-lived singleton that manages loading and caching the React manifest.json and collecting all required JS and CSS assets for the app’s entry point.

After instantiation, the only public attribute used is reactapp_build_assets, which returns ordered lists of the JS and CSS assets for the configured entry point. Its value is cached for the effective lifetime of the object instance (until server reboot).

This design ensures that asset collection is efficient and consistent, and that Django templates can reliably include the dependency-ordered asset files for each React app.

__init__(app_name, templatetag_name)[source]

Initialize the SmarterReactTemplateTagManager.

Parameters:
  • app_name (str) – The name of the app to manage template tags for.

  • templatetag_name (str) – The name of the template tag to register.

Raises:

SmarterValueError – If the app’s manifest.json has no entry point.

app_name: str
entry_key: str | None
find_entry_key()[source]

Locate the key of the manifest’s entry point, the chunk with "isEntry": true.

Example entry:

"index.html": {
    "file": "assets/index.js",
    "name": "index",
    "src": "index.html",
    "isEntry": true,
    "imports": [
        "_rolldown-runtime.js",
        "_xterm.js"
    ],
    "css": [
        "assets/index-DvLY75bJ.css"
    ]
}
Returns:

The entry point’s key, or None if there is no manifest.

Return type:

str | None

Raises:

SmarterValueError – If the manifest has no entry point.

property manifest: ViteManifest | None[source]

Load, validate and cache the app’s manifest.json.

A manifest.json that is missing, isn’t JSON, or doesn’t match ViteManifest is logged, and is cached as None, until the server restarts.

Returns:

The manifest, or None if it couldn’t be loaded.

Return type:

Optional[ViteManifest]

property manifest_path: str

The path of the app’s Vite-generated manifest.json in the static files directory.

Returns:

The manifest.json’s path.

Return type:

str

property reactapp_build_assets: ReactAppAssets[source]

The JS and CSS files of the app’s entry point, including all of its imports, in load order.

The entry point’s own files come first, followed by those of its imports, depth first. The result is cached for the lifetime of the manager.

Returns:

The entry point’s JS and CSS files. Both lists are empty if there is no manifest.

Return type:

ReactAppAssets

Example output:

{
    "js": [
        "assets/index-CZK_Bxxh.js",
        "assets/rolldown-runtime-B3igc2qu.js",
        "assets/xterm-D5XSfLrr.js"
    ],
    "css": [
        "assets/index-58MXwt-L.css",
        "assets/xterm-kHJ-D0s7.css"
    ]
}
templatetag_name: str

Contract

The Pydantic models of the Vite manifest.json that the manager reads, and of the assets that each React app’s template tag returns.

Pydantic models of the JSON that.

SmarterReactTemplateTagManager reads and returns.

  • ViteManifest is a Vite-generated React manifest.json: a map of ViteManifestChunk keyed by source path. The manager validates the file against it when it loads it, and collects an entry point’s assets with its methods.

  • ReactAppAssets is what every <app>_react_assets template tag returns: the ordered JS and CSS files that a Django template includes for a React app.

The manifest is a third-party shape. ViteManifestChunk declares the fields that Vite documents, and allows extra fields, which newer Vite (or Rolldown) versions add, so that a Vite upgrade doesn’t break page rendering. Smarter’s own addition, the _custom entry that every package’s vite.config.ts writes after the build, is a ReactAppBuildInfo.

smarter.lib.django.templatetags.smarter_react_contract.BUILD_INFO_KEY = '_custom'

The key of the build information that each React package’s vite.config.ts adds to its manifest.json.

class smarter.lib.django.templatetags.smarter_react_contract.ReactAppAssets(**data)[source]

Bases: BaseModel

The ordered JS and CSS files of a React app’s entry point, which a Django template includes.

Every <app>_react_assets template tag returns one. Templates read its attributes, e.g. {% for css_file in assets.css %}.

Example:

{
    "js": [
        "assets/index-CZK_Bxxh.js",
        "assets/rolldown-runtime-B3igc2qu.js",
        "assets/xterm-D5XSfLrr.js"
    ],
    "css": [
        "assets/index-58MXwt-L.css",
        "assets/xterm-kHJ-D0s7.css"
    ]
}
css: list[str]
js: list[str]
model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class smarter.lib.django.templatetags.smarter_react_contract.ReactAppBuildInfo(**data)[source]

Bases: BaseModel

The build information that each React package’s vite.config.ts adds to its manifest.json, as _custom.

Example:

"_custom": {
    "buildTime": "2026-10-12T01:09:47.988Z",
    "version": "0.3.0",
    "buildEnv": "production"
}
build_env: str
build_time: datetime
model_config = {'extra': 'forbid', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

version: str
smarter.lib.django.templatetags.smarter_react_contract.ReactAssetType

The kinds of asset that a React app’s entry point loads.

alias of Literal[‘js’, ‘css’]

class smarter.lib.django.templatetags.smarter_react_contract.ViteManifest(**data)[source]

Bases: BaseModel

A React app’s Vite-generated manifest.json: its chunks, keyed by source path, and Smarter’s build information.

In the file, the build information is the _custom entry, beside the chunks. Validating a manifest.json splits it out into build_info, and dumping the model puts it back, so model_dump(by_alias=True) has the file’s shape.

See the example in smarter.lib.django.templatetags.smarter_react_templatetag_manager.

build_info: ReactAppBuildInfo | None
chunks: dict[str, ViteManifestChunk]
collect_assets(key, asset_type, seen=None)[source]

Recursively collect the JS or CSS files of a chunk and of its static imports.

A chunk’s own files come first, followed by those of each of its imports, depth first, in the order they are listed. Each chunk is visited once, so circular imports terminate.

Parameters:
  • key (str) – The manifest key of the chunk to collect assets for.

  • asset_type (Literal['js', 'css']) – "js" for the chunks’ file, "css" for their css files.

  • seen (set[str] | None) – The keys already visited, used by the recursion.

Returns:

The asset file paths, relative to the app’s static directory.

Return type:

list[str]

entry_key()[source]

Find the key of the manifest’s entry point, the first chunk with "isEntry": true.

Returns:

The entry point’s key, or None if the manifest has no entry point.

Return type:

str | None

join_build_info(handler)[source]

Dump the manifest in the shape of manifest.json, with the build information as its _custom entry.

Parameters:

handler (SerializerFunctionWrapHandler) – Pydantic’s default serializer.

Returns:

The chunks, keyed by source path, and _custom.

Return type:

dict[str, Any]

model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

classmethod split_build_info(data)[source]

Split a manifest.json’s _custom entry from its chunks.

Input that already has chunks, e.g. keyword arguments, is left as is.

Parameters:

data (object) – The manifest.json’s content.

Returns:

The model’s fields.

Return type:

object

class smarter.lib.django.templatetags.smarter_react_contract.ViteManifestChunk(**data)[source]

Bases: BaseModel

One entry of a Vite-generated manifest.json: a chunk, or a static asset.

See https://vite.dev/guide/backend-integration. Extra fields are allowed, because Vite and Rolldown add their own.

Example:

"index.html": {
    "file": "assets/index.js",
    "name": "index",
    "src": "index.html",
    "isEntry": true,
    "imports": ["_rolldown-runtime.js", "_xterm.js"],
    "css": ["assets/index-DvLY75bJ.css"]
}
assets: list[str]
css: list[str]
dynamic_imports: list[str]
file: str
imports: list[str]
is_dynamic_entry: bool
is_entry: bool
model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

name: str | None
names: list[str]
src: str | None