Customise rendered static tags#

Pick this page when the tag markup must change, for example to add crossorigin or load from a CDN host. To resolve asset URLs through an external manifest, see Resolve asset URLs through a custom backend.

Problem#

You want collected assets to render with extra attributes such as crossorigin or to load from a CDN host.

Solution#

For attribute only changes, set the css_tag, js_tag, and module_tag options on the default backend. For URL rewriting, subclass StaticFilesBackend and override the renderer methods.

Walkthrough#

Attributes through options#

This customisation needs no Python. Bake the attributes into the tag format strings.

config/settings.py#
NEXT_FRAMEWORK = {
    "STATIC_BACKENDS": [
        {
            "BACKEND": "next.static.StaticFilesBackend",
            "OPTIONS": {
                "css_tag": '<link rel="stylesheet" href="{url}" crossorigin>',
                "js_tag": '<script src="{url}" defer crossorigin></script>',
                "module_tag": '<script type="module" src="{url}" crossorigin></script>',
            },
        }
    ]
}

The format string must contain the {url} placeholder.

Subclass for URL rewriting#

When the URL itself must change, subclass StaticFilesBackend and override the renderer methods.

notes/backends.py#
from next.static import StaticFilesBackend

CDN = "https://cdn.example.com"

class CdnBackend(StaticFilesBackend):
    def render_link_tag(self, url, *, request=None) -> str:
        return f'<link rel="stylesheet" href="{CDN}{url}">'

    def render_script_tag(self, url, *, request=None) -> str:
        return f'<script src="{CDN}{url}" defer></script>'

    def render_module_tag(self, url, *, request=None) -> str:
        return f'<script type="module" src="{CDN}{url}"></script>'

Each renderer method receives the URL and an optional request keyword.

Register the backend.

config/settings.py#
NEXT_FRAMEWORK = {
    "STATIC_BACKENDS": [
        {"BACKEND": "notes.backends.CdnBackend", "OPTIONS": {}}
    ]
}

Request aware output#

A renderer can read the request to vary its output per visitor.

notes/backends.py#
from next.static import StaticFilesBackend

class TenantBackend(StaticFilesBackend):
    def render_link_tag(self, url, *, request=None) -> str:
        prefix = getattr(getattr(request, "tenant", None), "cdn", "")
        return f'<link rel="stylesheet" href="{prefix}{url}">'

The static manager passes the current request to every renderer call.

Tenant URL prefix#

A common multi-tenant pattern is to prefix every collected URL with a tenant slug so static files are scoped per tenant. Override all three renderer methods and delegate to the parent after rewriting the URL. Leave absolute URLs untouched. The shipped multi-tenant example implements this pattern, and the snippet below mirrors its backend, overriding all three methods so every kind stays consistent.

notes/backends.py#
from next.static import StaticFilesBackend
from notes.access import get_active_tenant

PREFIX_FORMAT = "/_t/{slug}"

class TenantPrefixStaticBackend(StaticFilesBackend):
    def render_link_tag(self, url, *, request=None) -> str:
        return super().render_link_tag(_prefixed(url, request))

    def render_script_tag(self, url, *, request=None) -> str:
        return super().render_script_tag(_prefixed(url, request))

    def render_module_tag(self, url, *, request=None) -> str:
        return super().render_module_tag(_prefixed(url, request))

def _prefixed(url, request):
    tenant = get_active_tenant(request) if request is not None else None
    if tenant is None or not url.startswith("/"):
        return url
    return PREFIX_FORMAT.format(slug=tenant.slug) + url
config/settings.py#
NEXT_FRAMEWORK = {
    "STATIC_BACKENDS": [
        {"BACKEND": "notes.backends.TenantPrefixStaticBackend"},
    ],
}

get_active_tenant is a project-level helper that reads request.tenant set by middleware. See Scope requests per tenant for the full middleware and provider setup.

Verification#

Reload a page and inspect the HTML. Every <link> and <script> tag carries the new attributes or the CDN host.

Run uv run python manage.py check and confirm the backend is registered.

See also#

See also

Static backends for the backend contract. Asset kinds for the renderer methods. Resolve asset URLs through a custom backend for resolving URLs through an external manifest.