Rendering components
Your components can be rendered either within your Django templates, or directly in Python code.
Overviewยค
Django Components provides three main methods to render components:
{% component %}
tag - Renders the component within your Django templatesComponent.render()
method - Renders the component to a stringComponent.render_to_response()
method - Renders the component and wraps it in an HTTP response
{% component %}
tagยค
Use the {% component %}
tag to render a component within your Django templates.
The {% component %}
tag takes:
- Component's registered name as the first positional argument,
- Followed by any number of positional and keyword arguments.
To pass in slots content, you can insert {% fill %}
tags, directly within the {% component %}
tag to "fill" the slots:
{% component "my_table" rows=rows headers=headers %}
{% fill "pagination" %}
< 1 | 2 | 3 >
{% endfill %}
{% endcomponent %}
You can even nest {% fill %}
tags within {% if %}
, {% for %}
and other tags:
{% component "my_table" rows=rows headers=headers %}
{% if rows %}
{% fill "pagination" %}
< 1 | 2 | 3 >
{% endfill %}
{% endif %}
{% endcomponent %}
Omitting the component
keyword
If you would like to omit the component
keyword, and simply refer to your components by their registered names:
You can do so by setting the "shorthand" Tag formatter in the settings:
Extended template tag syntax
Unlike regular Django template tags, django-components' tags offer extra features like defining literal lists and dicts, and more. Read more about Template tag syntax.
Registering componentsยค
For a component to be renderable with the {% component %}
tag, it must be first registered with the @register()
decorator.
For example, if you register a component under the name "button"
:
from typing import NamedTuple
from django_components import Component, register
@register("button")
class Button(Component):
template_file = "button.html"
class Kwargs(NamedTuple):
name: str
job: str
def get_template_data(self, args, kwargs, slots, context):
...
Then you can render this component by using its registered name "button"
in the template:
As you can see above, the args and kwargs passed to the {% component %}
tag correspond to the component's input.
For more details, read Registering components.
Why do I need to register components?
TL;DR: To be able to share components as libraries, and because components can be registed with multiple registries / libraries.
Django-components allows to share components across projects.
However, different projects may use different settings. For example, one project may prefer the "long" format:
While the other may use the "short" format:
Both approaches are supported simultaneously for backwards compatibility, because django-components started out with only the "long" format.
To avoid ambiguity, when you use a 3rd party library, it uses the syntax that the author had configured for it.
So when you are creating a component, django-components need to know which registry the component belongs to, so it knows which syntax to use.
Rendering templatesยค
If you have embedded the component in a Django template using the {% component %}
tag:
{% load component_tags %}
<div>
{% component "calendar" date="2024-12-13" / %}
</div>
You can simply render the template with the Django's API:
-
from django.template import Template from django.template.loader import get_template # Either from a file template = get_template("my_template.html") # or inlined template = Template(""" {% load component_tags %} <div> {% component "calendar" date="2024-12-13" / %} </div> """) rendered_template = template.render()
Isolating componentsยค
By default, components behave similarly to Django's {% include %}
, and the template inside the component has access to the variables defined in the outer template.
You can selectively isolate a component, using the only
flag, so that the inner template can access only the data that was explicitly passed to it:
Alternatively, you can set all components to be isolated by default, by setting context_behavior
to "isolated"
in your settings:
render()
methodยค
The Component.render()
method renders a component to a string.
This is the equivalent of calling the {% component %}
tag.
from typing import NamedTuple, Optional
from django_components import Component, SlotInput
class Button(Component):
template_file = "button.html"
class Args(NamedTuple):
name: str
class Kwargs(NamedTuple):
surname: str
age: int
class Slots(NamedTuple):
footer: Optional[SlotInput] = None
def get_template_data(self, args, kwargs, slots, context):
...
Button.render(
args=["John"],
kwargs={
"surname": "Doe",
"age": 30,
},
slots={
"footer": "i AM A SLOT",
},
)
Component.render()
accepts the following arguments:
args
- Positional arguments to pass to the component (as a list or tuple)kwargs
- Keyword arguments to pass to the component (as a dictionary)slots
- Slot content to pass to the component (as a dictionary)context
- Django context for rendering (can be a dictionary or aContext
object)deps_strategy
- Dependencies rendering strategy (default:"document"
)request
- HTTP request object, used for context processors (optional)escape_slots_content
- Whether to HTML-escape slot content (default:True
)
All arguments are optional. If not provided, they default to empty values or sensible defaults.
See the API reference for Component.render()
for more details on the arguments.
render_to_response()
methodยค
The Component.render_to_response()
method works just like Component.render()
, but wraps the result in an HTTP response.
It accepts all the same arguments as Component.render()
.
Any extra arguments are passed to the HttpResponse
constructor.
from typing import NamedTuple, Optional
from django_components import Component, SlotInput
class Button(Component):
template_file = "button.html"
class Args(NamedTuple):
name: str
class Kwargs(NamedTuple):
surname: str
age: int
class Slots(NamedTuple):
footer: Optional[SlotInput] = None
def get_template_data(self, args, kwargs, slots, context):
...
# Render the component to an HttpResponse
response = Button.render_to_response(
args=["John"],
kwargs={
"surname": "Doe",
"age": 30,
},
slots={
"footer": "i AM A SLOT",
},
# Additional response arguments
status=200,
headers={"X-Custom-Header": "Value"},
)
This method is particularly useful in view functions, as you can return the result of the component directly:
def profile_view(request, user_id):
return Button.render_to_response(
kwargs={
"surname": "Doe",
"age": 30,
},
request=request,
)
Custom response classesยค
By default, Component.render_to_response()
returns a standard Django HttpResponse
.
You can customize this by setting the response_class
attribute on your component:
from django.http import HttpResponse
from django_components import Component
class MyHttpResponse(HttpResponse):
...
class MyComponent(Component):
response_class = MyHttpResponse
response = MyComponent.render_to_response()
assert isinstance(response, MyHttpResponse)
Dependencies renderingยค
The rendered HTML may be used in different contexts (browser, email, etc), and each may need different handling of JS and CSS scripts.
render()
and render_to_response()
accept a deps_strategy
parameter, which controls where and how the JS / CSS are inserted into the HTML.
The deps_strategy
parameter is ultimately passed to render_dependencies()
.
Learn more about Rendering JS / CSS.
There are six dependencies rendering strategies:
document
(default)- Smartly inserts JS / CSS into placeholders (
{% component_js_dependencies %}
) or into<head>
and<body>
tags. - Inserts extra script to allow
fragment
components to work. - Assumes the HTML will be rendered in a JS-enabled browser.
- Smartly inserts JS / CSS into placeholders (
fragment
- A lightweight HTML fragment to be inserted into a document with AJAX.
- Assumes the page was already rendered with
"document"
strategy. - No JS / CSS included.
simple
- Smartly insert JS / CSS into placeholders (
{% component_js_dependencies %}
) or into<head>
and<body>
tags. - No extra script loaded.
- Smartly insert JS / CSS into placeholders (
prepend
- Insert JS / CSS before the rendered HTML.
- Ignores the placeholders (
{% component_js_dependencies %}
) and any<head>
/<body>
HTML tags. - No extra script loaded.
append
- Insert JS / CSS after the rendered HTML.
- Ignores the placeholders (
{% component_js_dependencies %}
) and any<head>
/<body>
HTML tags. - No extra script loaded.
ignore
- HTML is left as-is. You can still process it with a different strategy later with
render_dependencies()
. - Used for inserting rendered HTML into other components.
- HTML is left as-is. You can still process it with a different strategy later with
Info
You can use the "prepend"
and "append"
strategies to force to output JS / CSS for components that don't have neither the placeholders like {% component_js_dependencies %}
, nor any <head>
/<body>
HTML tags:
rendered = Calendar.render_to_response(
request=request,
kwargs={
"date": request.GET.get("date", ""),
},
deps_strategy="append",
)
Renders something like this:
Passing contextยค
The render()
and render_to_response()
methods accept an optional context
argument. This sets the context within which the component is rendered.
When a component is rendered within a template with the {% component %}
tag, this will be automatically set to the Context instance that is used for rendering the template.
When you call Component.render()
directly from Python, there is no context object, so you can ignore this input most of the time. Instead, use args
, kwargs
, and slots
to pass data to the component.
However, you can pass RequestContext
to the context
argument, so that the component will gain access to the request object and will use context processors. Read more on Working with HTTP requests.
For advanced use cases, you can use context
argument to "pre-render" the component in Python, and then pass the rendered output as plain string to the template. With this, the inner component is rendered as if it was within the template with {% component %}
.
class Button(Component):
def render(self, context, template):
# Pass `context` to Icon component so it is rendered
# as if nested within Button.
icon = Icon.render(
context=context,
args=["icon-name"],
deps_strategy="ignore",
)
# Update context with icon
with context.update({"icon": icon}):
return template.render(context)
Warning
Whether the variables defined in context
are actually available in 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.
Therefore, it's strongly recommended to not rely on defining variables on the context object, but instead passing them through as args
and kwargs
โ Don't do this:
โ Do this:
Typing render methodsยค
Neither Component.render()
nor Component.render_to_response()
are typed, due to limitations of Python's type system.
To add type hints, you can wrap the inputs in component's Args
, Kwargs
, and Slots
classes.
Read more on Typing and validation.
from typing import NamedTuple, Optional
from django_components import Component, Slot, SlotInput
# Define the component with the types
class Button(Component):
class Args(NamedTuple):
name: str
class Kwargs(NamedTuple):
surname: str
age: int
class Slots(NamedTuple):
my_slot: Optional[SlotInput] = None
footer: SlotInput
# Add type hints to the render call
Button.render(
args=Button.Args(
name="John",
),
kwargs=Button.Kwargs(
surname="Doe",
age=30,
),
slots=Button.Slots(
footer=Slot(lambda *a, **kwa: "Click me!"),
),
)
Components as inputยค
django_components makes it possible to compose components in a "React-like" way, where you can render one component and use its output as input to another component:
from django.utils.safestring import mark_safe
# Render the inner component
inner_html = InnerComponent.render(
kwargs={"some_data": "value"},
deps_strategy="ignore", # Important for nesting!
)
# Use inner component's output in the outer component
outer_html = OuterComponent.render(
kwargs={
"content": mark_safe(inner_html), # Mark as safe to prevent escaping
},
)
The key here is setting deps_strategy="ignore"
for the inner component. This prevents duplicate rendering of JS / CSS dependencies when the outer component is rendered.
When deps_strategy="ignore"
:
- No JS or CSS dependencies will be added to the output HTML
- The component's content is rendered as-is
- The outer component will take care of including all needed dependencies
Read more about Rendering JS / CSS.
Dynamic componentsยค
Django components defines a special "dynamic" component (DynamicComponent
).
Normally, you have to hard-code the component name in the template:
The dynamic component allows you to dynamically render any component based on the is
kwarg. This is similar to Vue's dynamic components (<component :is>
).
{% component "dynamic" is=table_comp data=table_data headers=table_headers %}
{% fill "pagination" %}
{% component "pagination" / %}
{% endfill %}
{% endcomponent %}
The args, kwargs, and slot fills are all passed down to the underlying component.
As with other components, the dynamic component can be rendered from Python:
from django_components import DynamicComponent
DynamicComponent.render(
kwargs={
"is": table_comp,
"data": table_data,
"headers": table_headers,
},
slots={
"pagination": PaginationComponent.render(
deps_strategy="ignore",
),
},
)
Dynamic component nameยค
By default, the dynamic component is registered under the name "dynamic"
. In case of a conflict, you can set the COMPONENTS.dynamic_component_name
setting to change the name used for the dynamic components.
After which you will be able to use the dynamic component with the new name:
{% component "my_dynamic" is=table_comp data=table_data headers=table_headers %}
{% fill "pagination" %}
{% component "pagination" / %}
{% endfill %}
{% endcomponent %}
HTML fragmentsยค
Django-components provides a seamless integration with HTML fragments with AJAX (HTML over the wire), whether you're using jQuery, HTMX, AlpineJS, vanilla JavaScript, or other.
This is achieved by the combination of the "document"
and "fragment"
dependencies rendering strategies.
Read more about HTML fragments and Rendering JS / CSS.