Skip to content

HTML / JS / CSS variables

When a component recieves input through {% component %} tag, or the Component.render() or Component.render_to_response() methods, you can define how the input is handled, and what variables will be available to the template, JavaScript and CSS.

Overview¤

Django Components offers three key methods for passing variables to different parts of your component:

These methods let you pre-process inputs before they're used in rendering.

Each method handles the data independently - you can define different data for the template, JS, and CSS.

class ProfileCard(Component):
    class Kwargs(NamedTuple):
        user_id: int
        show_details: bool

    class Defaults:
        show_details = True

    def get_template_data(self, args, kwargs: Kwargs, slots, context):
        user = User.objects.get(id=kwargs.user_id)
        return {
            "user": user,
            "show_details": kwargs.show_details,
        }

    def get_js_data(self, args, kwargs: Kwargs, slots, context):
        return {
            "user_id": kwargs.user_id,
        }

    def get_css_data(self, args, kwargs: Kwargs, slots, context):
        text_color = "red" if kwargs.show_details else "blue"
        return {
            "text_color": text_color,
        }

Template variables¤

The get_template_data() method is the primary way to provide variables to your HTML template. It receives the component inputs and returns a dictionary of data that will be available in the template.

If get_template_data() returns None, an empty dictionary will be used.

class ProfileCard(Component):
    template_file = "profile_card.html"

    class Kwargs(NamedTuple):
        user_id: int
        show_details: bool

    def get_template_data(self, args, kwargs: Kwargs, slots, context):
        user = User.objects.get(id=kwargs.user_id)

        # Process and transform inputs
        return {
            "user": user,
            "show_details": kwargs.show_details,
            "user_joined_days": (timezone.now() - user.date_joined).days,
        }

In your template, you can then use these variables:

<div class="profile-card">
    <h2>{{ user.username }}</h2>

    {% if show_details %}
        <p>Member for {{ user_joined_days }} days</p>
        <p>Email: {{ user.email }}</p>
    {% endif %}
</div>

Legacy get_context_data()¤

The get_context_data() method is the legacy way to provide variables to your HTML template. It serves the same purpose as get_template_data() - it receives the component inputs and returns a dictionary of data that will be available in the template.

However, get_context_data() has a few drawbacks:

  • It does NOT receive the slots and context parameters.
  • The args and kwargs parameters are given as variadic *args and **kwargs parameters. As such, they cannot be typed.
class ProfileCard(Component):
    template_file = "profile_card.html"

    def get_context_data(self, user_id, show_details=False, *args, **kwargs):
        user = User.objects.get(id=user_id)
        return {
            "user": user,
            "show_details": show_details,
        }

There is a slight difference between get_context_data() and get_template_data() when rendering a component with the {% component %} tag.

For example if you have component that accepts kwarg date:

class MyComponent(Component):
    def get_context_data(self, date, *args, **kwargs):
        return {
            "date": date,
        }

    def get_template_data(self, args, kwargs, slots, context):
        return {
            "date": kwargs["date"],
        }

The difference is that:

  • With get_context_data(), you can pass date either as arg or kwarg:

    
    {% component "my_component" date=some_date %}
    {% component "my_component" some_date %}
    
  • But with get_template_data(), date MUST be passed as kwarg:

    
    {% component "my_component" date=some_date %}
    
    
    {% component "my_component" some_date %}
    

Warning

get_template_data() and get_context_data() are mutually exclusive.

If both methods return non-empty dictionaries, an error will be raised.

Note

The get_context_data() method will be removed in v2.

Accessing component inputs¤

The component inputs are available in two ways:

  1. Function arguments (recommended)

    The data methods receive the inputs as parameters, which you can access directly.

    class ProfileCard(Component):
        def get_template_data(self, args, kwargs, slots, context):
            # Access inputs directly as parameters
            return {
                "user_id": user_id,
                "show_details": show_details,
            }
    

    Info

    By default, the args parameter is a list, while kwargs and slots are dictionaries.

    If you add typing to your component with Args, Kwargs, or Slots classes, the respective inputs will be given as instances of these classes.

    Learn more about Component typing.

  2. self.input property

    The data methods receive only the main inputs. There are additional settings that may be passed to components. If you need to access these, you can do so via the self.input property.

    The input property contains all the inputs passed to the component (instance of ComponentInput).

    This includes:

    For more details, see Component inputs.

    class ProfileCard(Component):
        def get_template_data(self, args, kwargs, slots, context):
            # Access positional arguments
            user_id = self.input.args[0] if self.input.args else None
    
            # Access keyword arguments
            show_details = self.input.kwargs.get("show_details", False)
    
            # Render component differently depending on the type
            if self.input.type == "fragment":
                ...
    
            return {
                "user_id": user_id,
                "show_details": show_details,
            }
    

    Info

    Unlike the parameters passed to the data methods, the args, kwargs, and slots in self.input property are always lists and dictionaries, regardless of whether you added typing to your component.

Default values¤

You can use Defaults class to provide default values for your inputs.

These defaults will be applied either when:

  • The input is not provided at rendering time
  • The input is provided as None

When you then access the inputs in your data methods, the default values will be already applied.

Read more about Component Defaults.

from django_components import Component, Default, register

@register("profile_card")
class ProfileCard(Component):
    class Kwargs(NamedTuple):
        show_details: bool

    class Defaults:
        show_details = True

    # show_details will be set to True if `None` or missing
    def get_template_data(self, args, kwargs: Kwargs, slots, context):
        return {
            "show_details": kwargs.show_details,
        }

    ...

Warning

When typing your components with Args, Kwargs, or Slots classes, you may be inclined to define the defaults in the classes.

class ProfileCard(Component):
    class Kwargs(NamedTuple):
        show_details: bool = True

This is NOT recommended, because:

  • The defaults will NOT be applied to inputs when using self.input property.
  • The defaults will NOT be applied when a field is given but set to None.

Instead, define the defaults in the Defaults class.

Accessing Render API¤

All three data methods have access to the Component's Render API, which includes:

Type hints¤

Typing inputs¤

You can add type hints for the component inputs to ensure that the component logic is correct.

For this, define the Args, Kwargs, and Slots classes, and then add type hints to the data methods.

This will also validate the inputs at runtime, as the type classes will be instantiated with the inputs.

Read more about Component typing.

from typing import NamedTuple, Optional
from django_components import Component, SlotInput

class Button(Component):
    class Args(NamedTuple):
        name: str

    class Kwargs(NamedTuple):
        surname: str
        maybe_var: Optional[int] = None  # May be omitted

    class Slots(NamedTuple):
        my_slot: Optional[SlotInput] = None
        footer: SlotInput

    # Use the above classes to add type hints to the data method
    def get_template_data(self, args: Args, kwargs: Kwargs, slots: Slots, context: Context):
        # The parameters are instances of the classes we defined
        assert isinstance(args, Button.Args)
        assert isinstance(kwargs, Button.Kwargs)
        assert isinstance(slots, Button.Slots)

Note

The data available via self.input property is NOT typed.

Typing data¤

In the same fashion, you can add types and validation for the data that should be RETURNED from each data method.

For this, set the TemplateData, JsData, and CssData classes on the component class.

For each data method, you can either return a plain dictionary with the data, or an instance of the respective data class.

from typing import NamedTuple
from django_components import Component

class Button(Component):
    class TemplateData(NamedTuple):
        data1: str
        data2: int

    class JsData(NamedTuple):
        js_data1: str
        js_data2: int

    class CssData(NamedTuple):
        css_data1: str
        css_data2: int

    def get_template_data(self, args, kwargs, slots, context):
        return Button.TemplateData(
            data1="...",
            data2=123,
        )

    def get_js_data(self, args, kwargs, slots, context):
        return Button.JsData(
            js_data1="...",
            js_data2=123,
        )

    def get_css_data(self, args, kwargs, slots, context):
        return Button.CssData(
            css_data1="...",
            css_data2=123,
        )

Pass-through kwargs¤

It's best practice to explicitly define what args and kwargs a component accepts.

However, if you want a looser setup, you can easily write components that accept any number of kwargs, and pass them all to the template (similar to django-cotton).

To do that, simply return the kwargs dictionary itself from get_template_data():

class MyComponent(Component):
    def get_template_data(self, args, kwargs, slots, context):
        return kwargs

You can do the same for get_js_data() and get_css_data(), if needed:

class MyComponent(Component):
    def get_js_data(self, args, kwargs, slots, context):
        return kwargs

    def get_css_data(self, args, kwargs, slots, context):
        return kwargs