Components reference#
Module summary#
next.components exposes the component discovery, registration, and rendering API.
The names in this reference are grouped by their intended audience.
Note
The Application imports, Framework extension, and Internal infrastructure tiers follow the public-surface rules in Which symbols are safe to depend on.
Application imports#
These are the names project code uses day-to-day.
- next.components.component#
Registers and looks up context helpers used from component.py.
The component decorator namespace. Inside a
component.pyuse@component.context("key")to publish a value for the component template.
- next.components.context#
Mark a function so it fills template variables for this component module.
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.
The
@component.contextdecorator, bound fromComponentContextManager.context. It registers a context function inside acomponent.py.
- next.components.get_component(name: str, template_path: Path) ComponentInfo | None[source]#
Delegate to components_manager.get_component.
- next.components.collect_visible_components(template_path: Path) Mapping[str, ComponentInfo][source]#
Delegate to components_manager.collect_visible_components.
- next.components.load_component_template(info: ComponentInfo) str | None[source]#
Return raw template text for info.
- next.components.render_component(info: ComponentInfo, context_data: Mapping[str, Any], request: HttpRequest | None = None) str[source]#
Render info to HTML using template context and an optional request.
Manager#
backends is the configured list in consultation order, and reload rebuilds it from the current NEXT_FRAMEWORK.
Framework settings changes rebuild the manager on their own, so reload is for a caller that swaps COMPONENT_BACKENDS some other way, which is what next.testing.reset_components does.
- class next.components.ComponentsManager[source]#
Loads backends from settings and merges name resolution across them.
- property template_loader: ComponentTemplateLoader#
Return the shared ComponentTemplateLoader used for template reads.
- property component_renderer: ComponentRenderer#
Return the active ComponentRenderer with the configured strategies.
- reload() None[source]#
Rebuild the backends from the current NEXT_FRAMEWORK settings.
The render pipeline and the router-walk claims go with the old backends, so the next render resolves against the freshly configured sources.
- property backends: tuple[ComponentsBackend, ...]#
Return the configured backends in consultation order.
- register_router_walk_folder(folder: Path, pages_root: Path, scope_relative: str) None[source]#
Register components for one folder discovered during a page-tree walk.
The route trail the folder sits on becomes its scope, so the components resolve only for templates under that part of the tree. The folder goes to the first backend whose register_walked_folder claims it.
- get_component(name: str, template_path: Path) ComponentInfo | None[source]#
Return the first non-None match from configured backends.
- collect_visible_components(template_path: Path) Mapping[str, ComponentInfo][source]#
Merge visible names across backends so the first wins on duplicates.
- next.components.components_manager = <next.components.manager.ComponentsManager object>#
Loads backends from settings and merges name resolution across them.
Framework extension#
These names are used when writing a custom component backend or a custom renderer.
Backends#
ComponentsBackend.get_component and ComponentsBackend.collect_visible_components are the two abstract methods every backend implements, and the module-level helpers of the same name above delegate to them through the manager.
The rest of the contract has defaults that decline, so a backend implements only what its source can answer.
discover is the eager population pass, import_component_modules executes the components’ Python modules, register_walked_folder claims one components folder found during the page-tree walk, and iter_components with global_component_roots lets the system checks enumerate what the backend holds.
- class next.components.ComponentsBackend[source]#
Pluggable source of component definitions (files, database, etc.).
- abstractmethod get_component(name: str, template_path: Path) ComponentInfo | None[source]#
Return metadata for name from this backend, or None.
- abstractmethod collect_visible_components(template_path: Path) Mapping[str, ComponentInfo][source]#
Return a mapping of visible components for template_path.
- discover() None[source]#
Populate this backend from its source, once on app ready.
The default does nothing, which suits a backend that resolves names on demand.
- import_component_modules() tuple[Path, ...][source]#
Execute the module of every known component and return their paths.
Separate from discover, which only populates the registry, because LAZY_COMPONENT_MODULES leaves those modules unexecuted until a render needs one and a caller reading decorator state cannot wait.
- register_walked_folder(folder: Path, pages_root: Path, scope_relative: str) bool[source]#
Register folder under scope_relative below pages_root, or answer False.
The page-tree walk offers each components folder to the backends in configuration order and stops at the first that answers True, so one folder belongs to exactly one backend.
- iter_components() Iterable[ComponentInfo][source]#
Return every component this backend has registered, for diagnostics.
The system checks enumerate components through this to report duplicate names and wrong-decorator modules, which the render contract alone cannot answer.
- class next.components.FileComponentsBackend(config: dict[str, Any])[source]#
Load components from DIRS and from the filesystem walk in next.urls.
- __init__(config: dict[str, Any]) None[source]#
Build registry and scanner from the merged DIRS roots.
COMPONENTS_DIR is not read here. It names the folder the URL router skips inside a page tree, and FileRouterBackend reads it straight from the settings.
- import_component_modules() tuple[Path, ...][source]#
Import every discovered component.py and return their paths.
The import is deliberately unconditional, which is why a caller that walks decorator state pays under LAZY_COMPONENT_MODULES the import that the lazy mode otherwise avoids.
- register_walked_folder(folder: Path, pages_root: Path, scope_relative: str) bool[source]#
Scan the folder into the registry and claim it for this backend.
- iter_components() Iterable[ComponentInfo][source]#
Return every discovered component, scanning the roots first.
- global_component_roots() Iterable[Path][source]#
Return the DIRS roots whose components resolve from every template.
- get_component(name: str, template_path: Path) ComponentInfo | None[source]#
Return the named component visible from template_path.
- collect_visible_components(template_path: Path) Mapping[str, ComponentInfo][source]#
Return the full visibility map for template_path.
- next.components.register_components_folder_from_router_walk(folder: Path, pages_root: Path, scope_relative: str) None[source]#
Register components for one folder into the live components manager.
The URL router calls this during the page-tree walk and application code does not invoke it directly.
It registers into the live components_manager, which claims the folder on first registration, so a repeated walk over the same folder finds nothing left to do.
The manager offers the folder to each backend in configuration order and stops at the first whose register_walked_folder answers True.
The system checks perform the same registration through ComponentsManager.register_router_walk_folder on the manager they read, so a check run sees every page-tree component without waiting for a router walk and without writing into the live manager.
Context pipeline#
- class next.components.ComponentContextManager[source]#
Registers and looks up context helpers used from component.py.
- context(func_or_key: C, /) C[source]#
- context(func_or_key: str | None = None, *, serialize: bool = False, serializer: JsContextSerializer | None = None) Callable[[C], C]
Mark a function so it fills template variables for this component module.
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.
- get_functions(component_path: Path) Sequence[ContextFunction][source]#
Return context callables registered for this component.py path.
- class next.components.ComponentContextRegistry[source]#
Maps component.py paths to functions that supply template variables.
- 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 @component.context declared outside the running file.
The registration binds to declared_in, which no render of registered_from reads, so the pair feeds the next.E075 diagnostic.
- registered_names() dict[Path, tuple[str, ...]][source]#
Return the callable names registered per file, for the diagnostics.
- register(component_path: Path, key: str | None, func: Callable[..., Any], *, serialize: bool = False, serializer: JsContextSerializer | None = None) None[source]#
Register func under key for component_path, rejecting reserved keys.
- get_functions(component_path: Path) Sequence[ContextFunction][source]#
Return a tuple of registered context functions for component_path.
Renderers#
- class next.components.ComponentRenderer(strategies: Sequence[ComponentRenderStrategy])[source]#
Picks the first renderer that accepts this component.
- __init__(strategies: Sequence[ComponentRenderStrategy]) None[source]#
Bind the renderer to an ordered list of render strategies.
- class next.components.ComponentRenderStrategy(*args, **kwargs)[source]#
Optional render path for a ComponentInfo.
- can_render(info: ComponentInfo) bool[source]#
Return True when this strategy handles info.
- render(info: ComponentInfo, context_data: Mapping[str, Any], request: HttpRequest | None) str[source]#
Return the rendered HTML for info.
- __init__(*args, **kwargs)#
- class next.components.SimpleComponentRenderer(template_loader: ComponentTemplateLoader)[source]#
Uses the template string only (no component.py).
- __init__(template_loader: ComponentTemplateLoader) None[source]#
Bind this renderer to a shared ComponentTemplateLoader.
- can_render(info: ComponentInfo) bool[source]#
Return True for simple components and for missing module files.
- class next.components.CompositeComponentRenderer(module_loader: ModuleLoader, template_loader: ComponentTemplateLoader)[source]#
Uses render() in component.py when present, otherwise the template.
- __init__(module_loader: ModuleLoader, template_loader: ComponentTemplateLoader) None[source]#
Bind the renderer to shared module and template loaders.
- can_render(info: ComponentInfo) bool[source]#
Return True for composite components with a loadable component.py.
- class next.components.ComponentTemplateLoader(module_loader: ModuleLoader)[source]#
Read template source from a .djx file or a component module string.
- __init__(module_loader: ModuleLoader) None[source]#
Bind this loader to a shared ModuleLoader.
- load(info: ComponentInfo) str | None[source]#
Return raw template text for info or None when unavailable.
ComponentsManager wires a single ComponentTemplateLoader into its render pipeline.
The loader is fixed and not pluggable, so a custom backend reads component template bodies through this class rather than substituting its own.
Internal infrastructure#
These classes are implementation details. They are exported for testing and advanced instrumentation. Prefer the Application imports tier unless you are building framework tooling.
- class next.components.ComponentInfo(name: str, scope_root: Path, scope_relative: str, template_path: Path | None, module_path: Path | None, is_simple: bool)[source]#
What we know about one component after scanning the filesystem.
- scope_root: Path#
- resolved_scope_root: Path#
The duplicate-name check groups by (scope_root, scope_relative, name), the same pair the visibility resolver scores on, so only two components the resolver cannot tell apart collide under next.E020.
The same name under a deeper route trail of one tree is the documented override and stays silent.
- class next.components.ContextFunction(func: Callable[..., Any], key: str | None, serialize: bool = False, serializer: JsContextSerializer | None = None)[source]#
One function registered to add variables before a component template runs.
The optional serializer overrides the global JS context serializer for the value this callable produces, but only when serialize is true.
- func: Callable[..., Any]#
- serializer: JsContextSerializer | None#
- class next.components.ComponentRegistry[source]#
Holds discovered components and whether a directory is a global root.
- register(component: ComponentInfo) None[source]#
Append one component and index it by name.
- register_many(components: Iterable[ComponentInfo]) None[source]#
Index every component from the iterable in order.
Follows the Django bulk convention (bulk_create skips per-instance post_save). Receivers that need per-item events should subscribe to components_registered and read the infos tuple. The singular component_registered is not fired from this path.
- get_all() Sequence[ComponentInfo][source]#
Return an immutable view of every registered component.
- global_roots() frozenset[Path][source]#
Return every path marked as a global root, as an immutable set.
- __iter__() Iterator[ComponentInfo][source]#
Iterate over components in registration order.
- class next.components.ComponentVisibilityResolver(registry: ComponentRegistry)[source]#
Decides which component names exist for a given template file path.
- __init__(registry: ComponentRegistry) None[source]#
Bind the resolver to a ComponentRegistry and allocate caches.
- resolve_visible(template_path: Path) Mapping[str, ComponentInfo][source]#
Return a mapping of visible component names for template_path.
- class next.components.ModuleCache(maxsize: int = 128)[source]#
Remembers loaded Python modules by file path and drops the oldest when full.
- get(path: Path) ModuleType | object | None[source]#
Return the cached module, a cached None, or the miss sentinel.
- class next.components.ModuleLoader(cache: ModuleCache | None = None)[source]#
Loads a .py file as a module and reuses the last load for the same path.
- __init__(cache: ModuleCache | None = None) None[source]#
Bind the loader to a shared or new ModuleCache.
- class next.components.ComponentScanner(*, module_loader: ModuleLoader | None = None)[source]#
Scan one folder for .djx files and composite component directories.
- __init__(*, module_loader: ModuleLoader | None = None) None[source]#
Wire a module loader for composite component.py files.
- scan_directory(directory: Path, scope_root: Path, scope_relative: str) Sequence[ComponentInfo][source]#
Return a list of ComponentInfo found immediately inside directory.
- next.components.component_extra_roots_from_config(config: Mapping[str, Any]) list[Path][source]#
Return existing directory paths from the config DIRS entry.
- next.components.get_component_paths_for_watch() set[Path][source]#
Return filesystem paths that matter for the dev component reloader.
The scan mutates neither the components manager nor the router registry.
Test doubles#
DummyBackend and BoomBackend are minimal ComponentsBackend implementations kept in this module so that dotted-path resolution in tests works through the standard loader.
They are not intended for production use.
DummyBackend accepts a config dict, stores it on self, and resolves no components.
Use it to test backend wiring.
- class next.components.DummyBackend(config: dict[str, Any])[source]#
Test double that keeps its settings config entry on self.
- get_component(_name: str, _template_path: Path) ComponentInfo | None[source]#
Return None to skip name resolution through this backend.
- collect_visible_components(_template_path: Path) Mapping[str, ComponentInfo][source]#
Return an empty mapping because this test double never registers.
BoomBackend raises RuntimeError from __init__ so you can assert that a backend bug reaches the caller instead of being logged as a configuration error.
- class next.components.BoomBackend(config: dict[str, Any])[source]#
Test double that raises from __init__ for load error-path tests.
- get_component(_name: str, _template_path: Path) ComponentInfo | None[source]#
Unreachable because construction always raises.
- collect_visible_components(_template_path: Path) Mapping[str, ComponentInfo][source]#
Unreachable because construction always raises.
Signals#
See Signals reference and Signals for the components signals.
The module next.components.signals exposes four django.dispatch.Signal instances.
Signal |
Sender |
Payload |
|---|---|---|
|
|
|
|
|
|
|
The component backend class |
|
|
|
|
See also#
See also
Components for the topic guide. Extending for custom backends and render hooks. Testing for rendering components in isolation. Component pipeline for the discovery and render pipeline.