Pages reference#

Module summary#

next.pages exposes the page module API, the @context decorator, and the layout composition helpers.

Public API#

class next.pages.Page[source]#

Coordinate template loading, context, layouts, rendering, and URL wiring.

__init__() None[source]#

Initialise fresh registries and the layout loader.

File-based template loaders are not held as an instance attribute. The module-level build_registered_loaders() helper caches them and invalidates on settings_reloaded.

register_template(file_path: Path, template_str: str) None[source]#

Store rendered template source for file_path.

The compiled-template entry is dropped alongside so every write to the source registry invalidates the compiled layer with it.

clear_template_caches() None[source]#

Drop the composed source, the compiled templates, and the mtimes.

A caller that rewrites a page or a layout in place inside one process needs this, because the composed source is memoised per page path and the staleness check only reruns when a recorded mtime moves.

context(func_or_key: C, /) C[source]#
context(func_or_key: str | None = None, *, inherit_context: bool = False, serialize: bool = False, serializer: JsContextSerializer | None = None) Callable[[C], C]

Register a keyed or dict-merge @context for the file declaring func.

Pass serialize=True to include the return value in Next.context so JavaScript code on the page can read it via window.Next.context. Pass serializer= to route this key through a custom JsContextSerializer instead of the global JS_CONTEXT_SERIALIZER setting.

build_render_context(file_path: Path, request: HttpRequest | None = None, **kwargs) dict[str, object][source]#

Build the full render context dict used by render.

The returned dict includes _next_js_context holding the subset of values marked serialize=True. render pops that key and seeds the StaticCollector with it before creating the Django template context.

render_with_static_assets(file_path: Path, template: Template | str, context_data: dict[str, object], *, request: HttpRequest | None = None) tuple[str, StaticCollector][source]#

Render template and inject collected static assets.

template is either a precompiled Template (reused as-is) or raw template source, which is parsed before rendering.

The method seeds a fresh StaticCollector, hydrates it with the JS context that build_render_context left under the _next_js_context key, discovers co-located assets for the page, renders the Django template, and replaces placeholders through default_manager.inject. The active request reaches the static backend so request-aware subclasses can rewrite URLs. Both the rendered HTML and the collector are returned so callers can reuse the collector for telemetry without a second rendering pass. Suitable for the canonical page render path and for partial paths such as form-error rerenders.

composed_template_for(file_path: Path) Template[source]#

Return the compiled composed template for the static body.

The composed source is cached in _template_registry and invalidated by source-mtime staleness. The compiled Template layer keys off the same registry, so both caches go stale together and a warm hit performs no file reads and no parsing.

render(file_path: Path, request: HttpRequest | None = None, **kwargs) str[source]#

Render the page with Django Template and the static collector.

The static body source is the template attribute or any registered file-based TemplateLoader. The result is composed through the ancestor layout chain and cached compiled through composed_template_for. Direct callers of Page.render do not invoke render(). The unified view handles that path so dynamic bodies skip the registry cache.

authorization_outcome(file_path: Path, request: HttpRequest, **kwargs) tuple[HttpResponseBase | None, bool][source]#

Resolve a page body once, reporting its short-circuit and its kind.

render() runs under the same dependency injection as the unified view, so guards, denials, and redirects fire as they would on the page’s own request. An out-of-band zone morph reads the response and the dynamic flag from this one resolution, so the foreign page’s render() runs exactly once.

has_template(file_path: Path, module: types.ModuleType | None = None) bool[source]#

Return whether any source can supply a template for this path.

create_url_pattern(url_path: str, file_path: Path, url_parser: URLPatternParser) URLPattern | None[source]#

Return a path() pattern for a page, template, or virtual entry.

class next.pages.Context(source: object | None = None, *, default: object = <object object>)[source]#

Mark a parameter default so the value is taken from context_data.

An empty Context() reads the parameter name from context_data. A string source reads that context key. A callable source is called with DI-resolved arguments. Any other object becomes a constant. The default keyword supplies a fallback when the context key is missing.

source: object | None#
default: object#
__init__(source: object | None = None, *, default: object = <object object>) None#
class next.pages.ContextResult(context_data: dict[str, Any], js_context: dict[str, Any], js_context_serializers: dict[str, JsContextSerializer] = <factory>)[source]#

Hold the full template context and its JavaScript-serializable subset.

context_data contains every value merged into the Django template context. js_context contains only the subset marked serialize=True, which the renderer later hands to StaticCollector.add_js_context. js_context_serializers carries per-key serializer overrides supplied through @context(serializer=…) so the collector can route a single key through a custom serializer without affecting other keys.

context_data: dict[str, Any]#
js_context: dict[str, Any]#
js_context_serializers: dict[str, JsContextSerializer]#
__init__(context_data: dict[str, Any], js_context: dict[str, Any], js_context_serializers: dict[str, JsContextSerializer] = <factory>) None#
next.pages.context(func_or_key: Callable[..., Any] | str | None = None, *, inherit_context: bool = False, serialize: bool = False, serializer: JsContextSerializer | None = None) Callable[..., Any]#

Register a keyed or dict-merge @context for the file declaring func.

Pass serialize=True to include the return value in Next.context so JavaScript code on the page can read it via window.Next.context. Pass serializer= to route this key through a custom JsContextSerializer instead of the global JS_CONTEXT_SERIALIZER setting.

Note

The serialize and serializer keyword arguments opt a value into the JS context. See the topic guide for details.

next.pages.page = <next.pages.manager.Page object>#

Coordinate template loading, context, layouts, rendering, and URL wiring.

Manager#

Page manager and its process-wide singleton.

Page orchestrates template loading, context collection, layout composition, rendering, and URL-pattern wiring. page is the application-wide singleton. context is a convenience alias for page.context used by the @context decorator in user code.

class next.pages.manager.Page[source]#

Coordinate template loading, context, layouts, rendering, and URL wiring.

__init__() None[source]#

Initialise fresh registries and the layout loader.

File-based template loaders are not held as an instance attribute. The module-level build_registered_loaders() helper caches them and invalidates on settings_reloaded.

register_template(file_path: Path, template_str: str) None[source]#

Store rendered template source for file_path.

The compiled-template entry is dropped alongside so every write to the source registry invalidates the compiled layer with it.

clear_template_caches() None[source]#

Drop the composed source, the compiled templates, and the mtimes.

A caller that rewrites a page or a layout in place inside one process needs this, because the composed source is memoised per page path and the staleness check only reruns when a recorded mtime moves.

build_render_context(file_path: Path, request: HttpRequest | None = None, **kwargs) dict[str, object][source]#

Build the full render context dict used by render.

The returned dict includes _next_js_context holding the subset of values marked serialize=True. render pops that key and seeds the StaticCollector with it before creating the Django template context.

render_with_static_assets(file_path: Path, template: Template | str, context_data: dict[str, object], *, request: HttpRequest | None = None) tuple[str, StaticCollector][source]#

Render template and inject collected static assets.

template is either a precompiled Template (reused as-is) or raw template source, which is parsed before rendering.

The method seeds a fresh StaticCollector, hydrates it with the JS context that build_render_context left under the _next_js_context key, discovers co-located assets for the page, renders the Django template, and replaces placeholders through default_manager.inject. The active request reaches the static backend so request-aware subclasses can rewrite URLs. Both the rendered HTML and the collector are returned so callers can reuse the collector for telemetry without a second rendering pass. Suitable for the canonical page render path and for partial paths such as form-error rerenders.

composed_template_for(file_path: Path) Template[source]#

Return the compiled composed template for the static body.

The composed source is cached in _template_registry and invalidated by source-mtime staleness. The compiled Template layer keys off the same registry, so both caches go stale together and a warm hit performs no file reads and no parsing.

render(file_path: Path, request: HttpRequest | None = None, **kwargs) str[source]#

Render the page with Django Template and the static collector.

The static body source is the template attribute or any registered file-based TemplateLoader. The result is composed through the ancestor layout chain and cached compiled through composed_template_for. Direct callers of Page.render do not invoke render(). The unified view handles that path so dynamic bodies skip the registry cache.

authorization_outcome(file_path: Path, request: HttpRequest, **kwargs) tuple[HttpResponseBase | None, bool][source]#

Resolve a page body once, reporting its short-circuit and its kind.

render() runs under the same dependency injection as the unified view, so guards, denials, and redirects fire as they would on the page’s own request. An out-of-band zone morph reads the response and the dynamic flag from this one resolution, so the foreign page’s render() runs exactly once.

has_template(file_path: Path, module: types.ModuleType | None = None) bool[source]#

Return whether any source can supply a template for this path.

create_url_pattern(url_path: str, file_path: Path, url_parser: URLPatternParser) URLPattern | None[source]#

Return a path() pattern for a page, template, or virtual entry.

next.pages.manager.reset_context_registry() None[source]#

Clear the shared page-context registry for a from-disk rebuild.

The check-cache reset pairs this with the module memo so a re-executed page.py repopulates the registry from its current source.

next.pages.manager.iter_serialized_page_context_keys() Iterator[tuple[Path, str]][source]#

Yield the page.py path and key of every keyed serialize=True context.

A keyless serialize=True callable spreads the keys of the dict it returns at render time, so those keys exist only at runtime and never travel through here. One page reached through two spellings yields its keys once, under the spelling the registry keys on.

Registry#

Per-page.py context-callable registry and layout watch helpers.

PageContextRegistry stores the list of context functions bound to each page.py path, and merges their return values (with keyed and dict-merge semantics) at render time. The watch helpers list template.djx and layout.djx files under page roots for the autoreloader and for the static finder.

class next.pages.registry.PageContextEntry(func: Callable[..., Any], inherit_context: bool, serialize: bool, serializer: JsContextSerializer | None = None)[source]#

One context callable registered for a page.py file.

The optional serializer overrides the global JS context serializer for the value this callable produces, but only when serialize is true. Backed by NamedTuple so the hot register_context path allocates a plain tuple rather than a frozen dataclass instance.

func: Callable[..., Any]#

Alias for field number 0

inherit_context: bool#

Alias for field number 1

serialize: bool#

Alias for field number 2

serializer: JsContextSerializer | None#

Alias for field number 3

next.pages.registry.get_layout_djx_paths_for_watch() set[Path][source]#

Return every layout.djx path under page trees.

next.pages.registry.get_template_djx_paths_for_watch() set[Path][source]#

Return every template.djx path under page trees.

class next.pages.registry.PageContextRegistry(resolver: DependencyResolver | None = None)[source]#

Register per-page.py context callables and merge their output.

__init__(resolver: DependencyResolver | None = None) None[source]#

Initialise with an optional resolver and an empty registry.

reset() None[source]#

Drop every registered context so the next import repopulates it.

Re-executing a page.py only overwrites the keys it still declares, so a removed @context would otherwise leave a stale entry behind.

misattributed() tuple[MisattributedContext, ...][source]#

Return every registration bound to a file other than the one running it.

note_misattribution(registered_from: Path, declared_in: Path, func: Callable[..., Any]) None[source]#

Record a @context whose callable was declared outside the running file.

The registration binds to declared_in, which no render of registered_from reads, so the pair feeds the next.E074 diagnostic.

registered_names() dict[Path, tuple[str, ...]][source]#

Return the callable names registered per file, for the diagnostics.

register_context(file_path: Path, key: str | None, func: Callable[..., Any], *, inherit_context: bool = False, serialize: bool = False, serializer: JsContextSerializer | None = None) None[source]#

Bind func to file_path with keyed or dict-merge semantics.

collect_context(file_path: Path, request: HttpRequest | None = None, **kwargs) ContextResult[source]#

Merge inherited ancestor page.py context with this file’s context callables.

Inherited context comes from @context(..., inherit_context=True) callables in ancestor page.py files, not from layout files. The returned ContextResult separates the full template context from the JavaScript-serializable subset. The js_context uses first-registration semantics so that page-level values always take priority over inherited ones.

Loaders#

TemplateLoader is the abstract contract for sourcing template text from a page.py path.

class next.pages.loaders.TemplateLoader[source]#

Pluggable source of template text for a page.py path.

Subclasses set source_name to the filename they back. Typical values are “template.djx” or “template.md”. The name is surfaced in the next.W043 body-source conflict check.

source_name: ClassVar[str] = ''#
abstractmethod can_load(file_path: Path) bool[source]#

Return whether this loader applies without heavy work.

abstractmethod load_template(file_path: Path) str | None[source]#

Return the template source. Return None when unavailable.

source_path(file_path: Path) Path | None[source]#

Return the filesystem path this loader reads for file_path.

The page manager uses the result to snapshot file mtimes for stale-cache detection. The default returns None for non-file-based loaders. Subclasses override when they back a sibling file.

DjxTemplateLoader reads a sibling template.djx next to page.py. It is the only loader in the default TEMPLATE_LOADERS chain.

class next.pages.loaders.DjxTemplateLoader[source]#

Load from a sibling template.djx next to page.py.

source_name: ClassVar[str] = 'template.djx'#
can_load(file_path: Path) bool[source]#

Return whether sibling template.djx exists.

load_template(file_path: Path) str | None[source]#

Return the file contents of template.djx.

source_path(file_path: Path) Path | None[source]#

Return the sibling template.djx path for stale-cache detection.

PythonTemplateLoader reads a template attribute defined inside page.py. It is not registered by default. Add its dotted path to NEXT_FRAMEWORK["TEMPLATE_LOADERS"] to enable it. The manager already consults module.template directly, so registering this loader changes nothing at render time and only affects how the next.W043 conflict check reports the source.

class next.pages.loaders.PythonTemplateLoader[source]#

Load from page.py when the module defines a template attribute.

source_name: ClassVar[str] = 'template'#
can_load(file_path: Path) bool[source]#

Return whether the module loads and defines template.

load_template(file_path: Path) str | None[source]#

Return module.template if the module exposes it.

LayoutTemplateLoader composes nested layout.djx wrappers around the page template, walking every ancestor directory upward from the page, bounded at 64 levels. It runs on a dedicated path and is not registered through TEMPLATE_LOADERS.

class next.pages.loaders.LayoutTemplateLoader[source]#

Compose nested layout.djx wrappers around the page template.

can_load(file_path: Path) bool[source]#

Return whether at least one layout.djx exists on the path.

load_template(file_path: Path) str | None[source]#

Return the composed template with the page inside the innermost slot.

compose_body(body: str, file_path: Path) str[source]#

Wrap body through the ancestor layout chain for file_path.

Returns body verbatim when no layouts apply. When a sibling layout.djx exists the innermost layout owns the {% block template %} slot, so body is substituted as-is. Otherwise body is wrapped in a {% block template %} block before substitution so the ancestor layout’s placeholder remains a valid block.

LayoutTemplateLoader keeps no cache of its own. Composition results live on Page, where composed_template_for stores the composed source alongside the compiled Template, so a warm render parses nothing and opens no template file. It still stats every source file behind the page to detect a change. Both layers are dropped together once a template.djx or layout.djx changes on disk. A page whose body comes from a module-level render() in page.py bypasses that cache and recomposes the layout chain on every request. Page.clear_template_caches drops both layers and the mtime snapshots together, for a caller rewriting a page or a layout in place inside one process.

Module reads#

read_module_string_lists executes a page-tree module and returns the named module-level string lists it declares, or None when the file does not load. The static discovery layer reads the styles and scripts lists of a page.py or a component.py through it. Anything but a list or tuple of non-empty strings reads as an empty list, so the caller never type-checks what a user module bound to the name.

next.pages.loaders.read_module_string_lists(file_path: Path, attrs: Iterable[str]) dict[str, list[str]] | None[source]#

Return the named module-level string lists a page-tree module declares.

Answers None when the file does not execute as a module, which tells an absent or broken module apart from one that declares none of the names. Anything but a list or tuple of non-empty strings reads as an empty list, so a caller never has to type-check what a user module bound to the name.

Import failures#

A page.py the loader cannot read, an OSError or a module spec that does not build, counts as a legitimately absent module and the page contributes no body without a log record. A page.py whose body raises any exception during execution is a broken module, and logger.exception records the traceback on every load attempt. ImportError, AttributeError, and SyntaxError are common examples, not a closed list. On the request path the recorded failure re-raises as PageModuleImportError when settings.DEBUG or NEXT_FRAMEWORK["STRICT_LOADING"] is set. Under DEBUG the standard technical 500 page points at the failing line. Under STRICT_LOADING without DEBUG the client receives a generic 500 while the traceback stays in the server log. With both flags off the request answers 404, and the failure is visible only in the log record. An out-of-band zone morph of a broken foreign page raises PageModuleImportError in every mode, because the request that a 404 would answer belongs to another URL. The failure is scoped to the broken page. Sibling pages keep their URL patterns and keep serving in every mode, because the error surfaces at the view rather than while the urlconf is built. The recorded error is keyed by file mtime, so saving a fixed page.py clears it without a restart. manage.py check reports the same failure as next.E017, naming the exception type and message.

class next.pages.loaders.PageModuleImportError(file_path: Path)[source]#

A page.py body raised while importing.

Covers any exception raised by the module body. ImportError, SyntaxError, and AttributeError are common examples, not a closed list. The original exception travels as __cause__ and the offending path as file_path.

__init__(file_path: Path) None[source]#

Compose the message from the failing path.

The message reads <path> failed to import, and the original exception travels as __cause__.

Processors#

Context-processor discovery and loading.

Context processors come from two sources. First, each entry in NEXT_FRAMEWORK[“PAGE_BACKENDS”] may list processors under OPTIONS.context_processors. Second, Django’s TEMPLATES setting includes its own OPTIONS.context_processors. Both sources merge with Next-router entries taking precedence and duplicates dropped.

System checks#

next.pages.checks registers the Django system checks for the pages subsystem. They run through uv run python manage.py check.

The module exports eleven check callables.

  • check_context_functions.

  • check_context_processor_signature.

  • check_context_registration_files.

  • check_layout_templates.

  • check_page_functions.

  • check_page_module_imports.

  • check_pages_structure.

  • check_request_in_context.

  • check_single_keyless_context.

  • check_template_loaders.

  • check_unrouted_working_directory_pages.

See System checks for each check identifier, its condition, and the full autodoc of next.pages.checks.

Signals#

See Signals reference and Signals for the pages signals (template_loaded, context_registered, page_rendered).

See also#

See also

Pages for the topic guide. Page discovery for the internal pipeline.