Skip to content

Render API

When a component is being rendered, whether with Component.render() or {% component %}, a component instance is populated with the current inputs and context. This allows you to access things like component inputs.

We refer to these render-time-only methods and attributes as the "Render API".

Render API is available inside these Component methods:

Example:

class Table(Component):
    def on_render_before(self, context, template):
        # Access component's ID
        assert self.id == "c1A2b3c"

        # Access component's inputs, slots and context
        assert self.args == [123, "str"]
        assert self.kwargs == {"variable": "test", "another": 1}
        footer_slot = self.slots["footer"]
        some_var = self.context["some_var"]

    def get_template_data(self, args, kwargs, slots, context):
        # Access the request object and Django's context processors, if available
        assert self.request.GET == {"query": "something"}
        assert self.context_processors_data['user'].username == "admin"

rendered = Table.render(
    kwargs={"variable": "test", "another": 1},
    args=(123, "str"),
    slots={"footer": "MY_SLOT"},
)

Overview¤

The Render API includes:

Component inputs¤

Args¤

The args argument as passed to Component.get_template_data().

If you defined the Component.Args class, then the Component.args property will return an instance of that class.

Otherwise, args will be a plain list.

Use self.raw_args to access the positional arguments as a plain list irrespective of Component.Args.

Example:

With Args class:

from django_components import Component

class Table(Component):
    class Args(NamedTuple):
        page: int
        per_page: int

    def on_render_before(self, context: Context, template: Optional[Template]) -> None:
        assert self.args.page == 123
        assert self.args.per_page == 10

rendered = Table.render(
    args=[123, 10],
)

Without Args class:

from django_components import Component

class Table(Component):
    def on_render_before(self, context: Context, template: Optional[Template]) -> None:
        assert self.args[0] == 123
        assert self.args[1] == 10

Kwargs¤

The kwargs argument as passed to Component.get_template_data().

If you defined the Component.Kwargs class, then the Component.kwargs property will return an instance of that class.

Otherwise, kwargs will be a plain dictionary.

Use self.raw_kwargs to access the keyword arguments as a plain dictionary irrespective of Component.Kwargs.

Example:

With Kwargs class:

from django_components import Component

class Table(Component):
    class Kwargs(NamedTuple):
        page: int
        per_page: int

    def on_render_before(self, context: Context, template: Optional[Template]) -> None:
        assert self.kwargs.page == 123
        assert self.kwargs.per_page == 10

rendered = Table.render(
    kwargs={"page": 123, "per_page": 10},
)

Without Kwargs class:

from django_components import Component

class Table(Component):
    def on_render_before(self, context: Context, template: Optional[Template]) -> None:
        assert self.kwargs["page"] == 123
        assert self.kwargs["per_page"] == 10

Slots¤

The slots argument as passed to Component.get_template_data().

If you defined the Component.Slots class, then the Component.slots property will return an instance of that class.

Otherwise, slots will be a plain dictionary.

Use self.raw_slots to access the slots as a plain dictionary irrespective of Component.Slots.

Example:

With Slots class:

from django_components import Component, Slot, SlotInput

class Table(Component):
    class Slots(NamedTuple):
        header: SlotInput
        footer: SlotInput

    def on_render_before(self, context: Context, template: Optional[Template]) -> None:
        assert isinstance(self.slots.header, Slot)
        assert isinstance(self.slots.footer, Slot)

rendered = Table.render(
    slots={
        "header": "MY_HEADER",
        "footer": lambda ctx: "FOOTER: " + ctx.data["user_id"],
    },
)

Without Slots class:

from django_components import Component, Slot, SlotInput

class Table(Component):
    def on_render_before(self, context: Context, template: Optional[Template]) -> None:
        assert isinstance(self.slots["header"], Slot)
        assert isinstance(self.slots["footer"], Slot)

Context¤

The context argument as passed to Component.get_template_data().

This is Django's Context with which the component template is rendered.

If the root component or template was rendered with RequestContext then this will be an instance of RequestContext.

Whether the context variables defined in context are available to the template depends on the context behavior mode:

  • In "django" context behavior mode, the template will have access to the keys of this context.

  • In "isolated" context behavior mode, the template will NOT have access to this context, and data MUST be passed via component's args and kwargs.

Component ID¤

Component ID (or render ID) is a unique identifier for the current render call.

That means that if you call Component.render() multiple times, the ID will be different for each call.

It is available as self.id.

The ID is a 7-letter alphanumeric string in the format cXXXXXX, where XXXXXX is a random string of 6 alphanumeric characters (case-sensitive).

E.g. c1a2b3c.

A single render ID has a chance of collision 1 in 57 billion. However, due to birthday paradox, the chance of collision increases to 1% when approaching ~33K render IDs.

Thus, there is currently a soft-cap of ~30K components rendered on a single page.

If you need to expand this limit, please open an issue on GitHub.

class Table(Component):
    def get_template_data(self, args, kwargs, slots, context):
        # Access component's ID
        assert self.id == "c1A2b3c"

Request and context processors¤

Components have access to the request object and context processors data if the component was:

  • Given a request kwarg directly
  • Rendered with RenderContext
  • Nested in another component for which any of these conditions is true

Then the request object will be available in self.request.

If the request object is available, you will also be able to access the context processors data in self.context_processors_data.

This is a dictionary with the context processors data.

If the request object is not available, then self.context_processors_data will be an empty dictionary.

Read more about the request object and context processors in the HTTP Request section.

from django.http import HttpRequest

class Table(Component):
    def get_template_data(self, args, kwargs, slots, context):
        # Access the request object and Django's context processors
        assert self.request.GET == {"query": "something"}
        assert self.context_processors_data['user'].username == "admin"

rendered = Table.render(
    request=HttpRequest(),
)

Provide / Inject¤

Components support a provide / inject system as known from Vue or React.

When rendering the component, you can call self.inject() with the key of the data you want to inject.

The object returned by self.inject()

To provide data to components, use the {% provide %} template tag.

Read more about Provide / Inject.

class Table(Component):
    def get_template_data(self, args, kwargs, slots, context):
        # Access provided data
        data = self.inject("some_data")
        assert data.some_data == "some_data"

Template tag metadata¤

If the component is rendered with {% component %} template tag, the following metadata is available:

You can use these to check whether the component was rendered inside a template with {% component %} tag or in Python with Component.render().

class MyComponent(Component):
    def get_template_data(self, args, kwargs, slots, context):
        if self.registered_name is None:
            # Do something for the render() function
        else:
            # Do something for the {% component %} template tag

You can access the ComponentNode under Component.node:

class MyComponent(Component):
    def get_template_data(self, context, template):
        if self.node is not None:
            assert self.node.name == "my_component"

Accessing the ComponentNode is mostly useful for extensions, which can modify their behaviour based on the source of the Component.

For example, if MyComponent was used in another component - that is, with a {% component "my_component" %} tag in a template that belongs to another component - then you can use self.node.template_component to access the owner Component class.

class Parent(Component):
    template: types.django_html = """
        <div>
            {% component "my_component" / %}
        </div>
    """

@register("my_component")
class MyComponent(Component):
    def get_template_data(self, context, template):
        if self.node is not None:
            assert self.node.template_component == Parent

Info

Component.node is None if the component is created by Component.render() (but you can pass in the node kwarg yourself).