Extension hooks
Extensions hook into the component lifecycle by subclassing ComponentExtension and implementing the on_* methods below. Each hook receives a single context object whose fields are listed under Available data.
Hooks
on_component_class_created
on_component_class_created(ctx: OnComponentClassCreatedContext) -> NoneCalled when a new Component class is created.
This hook is called after the Component class is fully defined but before it's registered.
Use this hook to perform any initialization or validation of the Component class.
Example
from django_components import ComponentExtension, OnComponentClassCreatedContext
class MyExtension(ComponentExtension):
def on_component_class_created(self, ctx: OnComponentClassCreatedContext) -> None:
# Add a new attribute to the Component class
ctx.component_cls.my_attr = "my_value"
on_component_class_deleted
on_component_class_deleted(ctx: OnComponentClassDeletedContext) -> NoneCalled when a Component class is being deleted.
This hook is called before the Component class is deleted from memory.
Use this hook to perform any cleanup related to the Component class.
Example
from django_components import ComponentExtension, OnComponentClassDeletedContext
class MyExtension(ComponentExtension):
def on_component_class_deleted(self, ctx: OnComponentClassDeletedContext) -> None:
# Remove Component class from the extension's cache on deletion
self.cache.pop(ctx.component_cls, None)
on_component_data
on_component_data(ctx: OnComponentDataContext) -> NoneCalled when a Component was triggered to render, after a component's context and data methods have been processed.
This hook is called after Component.get_template_data(), Component.get_js_data() and Component.get_css_data().
This hook runs after on_component_input.
Use this hook to modify or validate the component's data before rendering.
Example
from django_components import ComponentExtension, OnComponentDataContext
class MyExtension(ComponentExtension):
def on_component_data(self, ctx: OnComponentDataContext) -> None:
# Add extra template variable to all components when they are rendered
ctx.template_data["my_template_var"] = "my_value"
Available data
| Field | Type | Description |
|---|---|---|
component | Component | The Component instance that is being rendered |
component_cls | type[Component] | The Component class |
component_id | str | The unique identifier for this component instance |
context_data | dict | Deprecated. Use template_data instead. Will be removed in v1.0. |
template_data | dict | Dictionary of template data from Component.get_template_data() |
js_data | dict | Dictionary of JavaScript data from Component.get_js_data() |
css_data | dict | Dictionary of CSS data from Component.get_css_data() |
on_component_input
on_component_input(ctx: OnComponentInputContext) -> str | NoneCalled when a Component was triggered to render, but before a component's context and data methods are invoked.
Use this hook to modify or validate component inputs before they're processed.
This is the first hook that is called when rendering a component. As such this hook is called before Component.get_template_data(), Component.get_js_data(), and Component.get_css_data() methods, and the on_component_data hook.
This hook also allows to skip the rendering of a component altogether. If the hook returns a non-null value, this value will be used instead of rendering the component.
You can use this to implement a caching mechanism for components, or define components that will be rendered conditionally.
Warning
When any extension short-circuits a component (by returning a non-null value), the rest of that component's render is skipped, including on_component_data and on_component_rendered.
Extensions run in order, and the built-in extensions (including the cache) run before user extensions. So your on_component_input may run even when a later extension short-circuits the same component.
In practice this means: if you save something at the start of a render (here, in on_component_input) so you can use or remove it later in on_component_rendered, that later hook might never run. And if you saved it in a dictionary that lives on your extension, nothing ever removes that entry, so the dictionary grows by one with every skipped render. That is a memory leak.
To avoid this, store anything you need for a single render on the component itself, or on something that lives only as long as the component (such as its config object for your extension, or Slot.extra). It is then discarded automatically once the component is done, whether or not on_component_rendered runs.
Example
from django_components import ComponentExtension, OnComponentInputContext
class MyExtension(ComponentExtension):
def on_component_input(self, ctx: OnComponentInputContext) -> None:
# Add extra kwarg to all components when they are rendered
ctx.kwargs["my_input"] = "my_value"
Warning
In this hook, the components' inputs are still mutable.
As such, if a component defines Args, Kwargs, Slots types, these types are NOT yet instantiated.
Instead, component fields like Component.args, Component.kwargs, Component.slots are plain list / dict objects.
Available data
| Field | Type | Description |
|---|---|---|
component | Component | The Component instance that received the input and is being rendered |
component_cls | type[Component] | The Component class |
component_id | str | The unique identifier for this component instance |
args | list | List of positional arguments passed to the component |
kwargs | dict | Dictionary of keyword arguments passed to the component |
slots | dict[str, Slot] | Dictionary of slot definitions |
context | Context | The Django template Context object |
on_component_registered
on_component_registered(ctx: OnComponentRegisteredContext) -> NoneCalled when a Component class is registered with a ComponentRegistry.
This hook is called after a Component class is successfully registered.
Example
from django_components import ComponentExtension, OnComponentRegisteredContext
class MyExtension(ComponentExtension):
def on_component_registered(self, ctx: OnComponentRegisteredContext) -> None:
print(f"Component {ctx.component_cls} registered to {ctx.registry} as '{ctx.name}'")
Available data
| Field | Type | Description |
|---|---|---|
registry | ComponentRegistry | The registry the component was registered to |
name | str | The name the component was registered under |
component_cls | type[Component] | The registered Component class |
on_component_rendered
on_component_rendered(ctx: OnComponentRenderedContext) -> str | NoneCalled when a Component was rendered, including all its child components.
Use this hook to access or post-process the component's rendered output.
This hook works similarly to Component.on_render_after():
To modify the output, return a new string from this hook. The original output or error will be ignored.
To cause this component to return a new error, raise that error. The original output and error will be ignored.
If you neither raise nor return string, the original output or error will be used.
Example
Change the final output of a component:
from django_components import ComponentExtension, OnComponentRenderedContext
class MyExtension(ComponentExtension):
def on_component_rendered(self, ctx: OnComponentRenderedContext) -> str | None:
# Append a comment to the component's rendered output
return ctx.result + "<!-- MyExtension comment -->"
Cause the component to raise a new exception:
from django_components import ComponentExtension, OnComponentRenderedContext
class MyExtension(ComponentExtension):
def on_component_rendered(self, ctx: OnComponentRenderedContext) -> str | None:
# Raise a new exception
raise Exception("Error message")
Return nothing (or None) to handle the result as usual:
from django_components import ComponentExtension, OnComponentRenderedContext
class MyExtension(ComponentExtension):
def on_component_rendered(self, ctx: OnComponentRenderedContext) -> str | None:
if ctx.error is not None:
# The component raised an exception
print(f"Error: {ctx.error}")
else:
# The component rendered successfully
print(f"Result: {ctx.result}")
Available data
| Field | Type | Description |
|---|---|---|
component | Component | The Component instance that is being rendered |
component_cls | type[Component] | The Component class |
component_id | str | The unique identifier for this component instance |
result | str | None | The rendered component, or None if rendering failed |
error | Exception | None | The error that occurred during rendering, or None if rendering was successful |
on_component_unregistered
on_component_unregistered(ctx: OnComponentUnregisteredContext) -> NoneCalled when a Component class is unregistered from a ComponentRegistry.
This hook is called after a Component class is removed from the registry.
Example
from django_components import ComponentExtension, OnComponentUnregisteredContext
class MyExtension(ComponentExtension):
def on_component_unregistered(self, ctx: OnComponentUnregisteredContext) -> None:
print(f"Component {ctx.component_cls} unregistered from {ctx.registry} as '{ctx.name}'")
Available data
| Field | Type | Description |
|---|---|---|
registry | ComponentRegistry | The registry the component was unregistered from |
name | str | The name the component was registered under |
component_cls | type[Component] | The unregistered Component class |
on_css_loaded
on_css_loaded(ctx: OnCssLoadedContext) -> str | NoneCalled when a Component's CSS is loaded as a string.
This hook runs only once per Component class and works for both Component.css and Component.css_file.
Use this hook to read or modify the CSS.
To modify the CSS, return a new string from this hook.
Example
from django_components import ComponentExtension, OnCssLoadedContext
class MyExtension(ComponentExtension):
def on_css_loaded(self, ctx: OnCssLoadedContext) -> str | None:
# Modify the CSS
return ctx.content.replace("Hello", "Hi")
on_dependencies
Called when a rendered HTML is being finalized, after all dependencies (JS and CSS) were collected, and before they are rendered as <script> and <link> tags.
Use this hook to access or modify the JS/CSS dependencies, for example to:
- Modify or add dependencies
- Render
<script>tags JS modules withtype="module" - Add CSP nonce to the dependencies
To modify the dependencies, return a tuple of (scripts, styles).
Where:
Example
from django_components import (
ComponentExtension,
OnDependenciesContext,
Script,
Style,
)
class MyExtension(ComponentExtension):
def on_dependencies(self, ctx: OnDependenciesContext) -> tuple[list["Script"], list["Style"]]:
scripts = ctx.scripts
styles = ctx.styles
# Modify existing scripts and styles
for script in scripts:
if script.kind == "extra":
script.wrap = False
for style in styles:
if style.kind == "extra":
style.attrs["media"] = "print"
# Add extra JS and CSS dependencies (inline content)
scripts.append(
Script(
content="console.log('extension-injected script');",
wrap=False,
)
)
styles.append(
Style(
content="body { background-color: red; }",
)
)
# Add extra JS and CSS dependencies (external URL)
scripts.append(
Script(
url="/static/analytics.js",
content=None,
)
)
styles.append(
Style(
url="/static/print.css",
content=None,
attrs={"media": "print"},
)
)
return (scripts, styles)
on_extension_created
on_extension_created(ctx: OnExtensionCreatedContext) -> NoneCalled when a new ComponentExtension instance is created.
Use this hook to perform any initialization or validation of the extension instance.
Example
from django_components import ComponentExtension, OnExtensionCreatedContext
class MyExtension(ComponentExtension):
def on_extension_created(self, ctx: OnExtensionCreatedContext) -> None:
# Add a new attribute to the extension instance
ctx.extension.my_attr = "my_value"
Available data
| Field | Type | Description |
|---|---|---|
extension | ComponentExtension | The created extension |
on_js_loaded
on_js_loaded(ctx: OnJsLoadedContext) -> str | NoneCalled when a Component's JS is loaded as a string.
This hook runs only once per Component class and works for both Component.js and Component.js_file.
Use this hook to read or modify the JS.
To modify the JS, return a new string from this hook.
Example
from django_components import ComponentExtension, OnCssLoadedContext
class MyExtension(ComponentExtension):
def on_js_loaded(self, ctx: OnJsLoadedContext) -> str | None:
# Modify the JS
return ctx.content.replace("Hello", "Hi")
on_registry_created
on_registry_created(ctx: OnRegistryCreatedContext) -> NoneCalled when a new ComponentRegistry is created.
This hook is called after a new ComponentRegistry instance is initialized.
Use this hook to perform any initialization needed for the registry.
Example
from django_components import ComponentExtension, OnRegistryCreatedContext
class MyExtension(ComponentExtension):
def on_registry_created(self, ctx: OnRegistryCreatedContext) -> None:
# Add a new attribute to the registry
ctx.registry.my_attr = "my_value"
Available data
| Field | Type | Description |
|---|---|---|
registry | ComponentRegistry | The created ComponentRegistry instance |
on_registry_deleted
on_registry_deleted(ctx: OnRegistryDeletedContext) -> NoneCalled when a ComponentRegistry is being deleted.
This hook is called before a ComponentRegistry instance is deleted.
Use this hook to perform any cleanup related to the registry.
Example
from django_components import ComponentExtension, OnRegistryDeletedContext
class MyExtension(ComponentExtension):
def on_registry_deleted(self, ctx: OnRegistryDeletedContext) -> None:
# Remove registry from the extension's cache on deletion
self.cache.pop(ctx.registry, None)
Available data
| Field | Type | Description |
|---|---|---|
registry | ComponentRegistry | The to-be-deleted ComponentRegistry instance |
on_slot_rendered
on_slot_rendered(ctx: OnSlotRenderedContext) -> str | NoneCalled when a {% slot %} tag was rendered.
Use this hook to access or post-process the slot's rendered output.
To modify the output, return a new string from this hook.
Example
from django_components import ComponentExtension, OnSlotRenderedContext
class MyExtension(ComponentExtension):
def on_slot_rendered(self, ctx: OnSlotRenderedContext) -> str | None:
# Append a comment to the slot's rendered output
return ctx.result + "<!-- MyExtension comment -->"
Access slot metadata:
You can access the {% slot %} tag node (SlotNode) and its metadata using ctx.slot_node.
For example, to find the Component class to which belongs the template where the {% slot %} tag is defined, you can use ctx.slot_node.template_component:
from django_components import ComponentExtension, OnSlotRenderedContext
class MyExtension(ComponentExtension):
def on_slot_rendered(self, ctx: OnSlotRenderedContext) -> str | None:
# Access slot metadata
slot_node = ctx.slot_node
slot_owner = slot_node.template_component
print(f"Slot owner: {slot_owner}")
Available data
| Field | Type | Description |
|---|---|---|
component | Component | The Component instance that contains the {% slot %} tag |
component_cls | type[Component] | The Component class that contains the {% slot %} tag |
component_id | str | The unique identifier for this component instance |
slot | Slot | The Slot instance that was rendered |
slot_name | str | The name of the {% slot %} tag |
slot_node | SlotNode | The node instance of the {% slot %} tag |
slot_is_required | bool | Whether the slot is required |
slot_is_default | bool | Whether the slot is default |
result | SlotResult | The rendered result of the slot |
on_template_compiled
on_template_compiled(ctx: OnTemplateCompiledContext) -> NoneCalled when a Component's template is compiled into a Template object.
This hook runs only once per Component class and works for both Component.template and Component.template_file.
Use this hook to read or modify the template (in-place) after it's compiled.
Example
from django_components import ComponentExtension, OnTemplateCompiledContext
class MyExtension(ComponentExtension):
def on_template_compiled(self, ctx: OnTemplateCompiledContext) -> None:
print(f"Template origin: {ctx.template.origin.name}")
on_template_loaded
on_template_loaded(ctx: OnTemplateLoadedContext) -> str | NoneCalled when a Component's template is loaded as a string.
This hook runs only once per Component class and works for both Component.template and Component.template_file.
Use this hook to read or modify the template before it's compiled.
To modify the template, return a new string from this hook.
Example
from django_components import ComponentExtension, OnTemplateLoadedContext
class MyExtension(ComponentExtension):
def on_template_loaded(self, ctx: OnTemplateLoadedContext) -> str | None:
# Modify the template
return ctx.content.replace("Hello", "Hi")
Objects
OnComponentDataContext
Fields
| Field | Type | Description |
|---|---|---|
component | Component | The Component instance that is being rendered |
component_cls | type[Component] | The Component class |
component_id | str | The unique identifier for this component instance |
context_data | dict | Deprecated. Use template_data instead. Will be removed in v1.0. |
template_data | dict | Dictionary of template data from Component.get_template_data() |
js_data | dict | Dictionary of JavaScript data from Component.get_js_data() |
css_data | dict | Dictionary of CSS data from Component.get_css_data() |
OnComponentInputContext
Fields
| Field | Type | Description |
|---|---|---|
component | Component | The Component instance that received the input and is being rendered |
component_cls | type[Component] | The Component class |
component_id | str | The unique identifier for this component instance |
args | list | List of positional arguments passed to the component |
kwargs | dict | Dictionary of keyword arguments passed to the component |
slots | dict[str, Slot] | Dictionary of slot definitions |
context | Context | The Django template Context object |
OnComponentRegisteredContext
Fields
| Field | Type | Description |
|---|---|---|
registry | ComponentRegistry | The registry the component was registered to |
name | str | The name the component was registered under |
component_cls | type[Component] | The registered Component class |
OnComponentRenderedContext
Fields
| Field | Type | Description |
|---|---|---|
component | Component | The Component instance that is being rendered |
component_cls | type[Component] | The Component class |
component_id | str | The unique identifier for this component instance |
result | str | None | The rendered component, or None if rendering failed |
error | Exception | None | The error that occurred during rendering, or None if rendering was successful |
OnComponentUnregisteredContext
Fields
| Field | Type | Description |
|---|---|---|
registry | ComponentRegistry | The registry the component was unregistered from |
name | str | The name the component was registered under |
component_cls | type[Component] | The unregistered Component class |
OnExtensionCreatedContext
Fields
| Field | Type | Description |
|---|---|---|
extension | ComponentExtension | The created extension |
OnRegistryCreatedContext
Fields
| Field | Type | Description |
|---|---|---|
registry | ComponentRegistry | The created ComponentRegistry instance |
OnRegistryDeletedContext
Fields
| Field | Type | Description |
|---|---|---|
registry | ComponentRegistry | The to-be-deleted ComponentRegistry instance |
OnSlotRenderedContext
Fields
| Field | Type | Description |
|---|---|---|
component | Component | The Component instance that contains the {% slot %} tag |
component_cls | type[Component] | The Component class that contains the {% slot %} tag |
component_id | str | The unique identifier for this component instance |
slot | Slot | The Slot instance that was rendered |
slot_name | str | The name of the {% slot %} tag |
slot_node | SlotNode | The node instance of the {% slot %} tag |
slot_is_required | bool | Whether the slot is required |
slot_is_default | bool | Whether the slot is default |
result | SlotResult | The rendered result of the slot |