django-components is a modular and extensible UI framework for Django.
It combines Django's templating system with the modularity seen in modern frontend frameworks like Vue or React.
With django-components you can support Django projects small and large without leaving the Django ecosystem.
django-components is tested across all major browsers - Chromium, Firefox, WebKit ✅.
Sponsors
Quickstart
A component in django-components consists of HTML, JavaScript, and CSS:
# components/product_card/product_card.py from django_components import Component, register @register("product_card") class ProductCard(Component): template_file = "product_card.html" js_file = "product_card.js" css_file = "product_card.css" class Kwargs: product_id: int show_price: bool = True theme: str = "light" def get_template_data(self, args, kwargs: Kwargs, slots, context): product = Product.objects.get(id=kwargs.product_id) return { "product": product, "show_price": kwargs.show_price, "is_in_stock": product.stock_count > 0, } def get_js_data(self, args, kwargs: Kwargs, slots, context): product = Product.objects.get(id=kwargs.product_id) return { "product_id": kwargs.product_id, "price": float(product.price), "api_endpoint": f"/api/products/{kwargs.product_id}/", } def get_css_data(self, args, kwargs: Kwargs, slots, context): themes = { "light": { "card_bg": "#ffffff", "text_color": "#333333", "price_color": "#e63946", }, "dark": { "card_bg": "#242424", "text_color": "#f1f1f1", "price_color": "#ff6b6b", }, } theme_vars = themes.get(kwargs.theme, themes["light"]) return theme_vars
In your template:
{# templates/product_card/product_card.html #} <div class="product-card" data-product-id="{{ product.id }}"> <img src="{{ product.image_url }}" alt="{{ product.name }}"> <h3>{{ product.name }}</h3> {% if show_price %} <p class="price">${{ product.price }}</p> {% endif %} {% if is_in_stock %} <button class="add-to-cart">Add to Cart</button> {% else %} <p class="out-of-stock">Out of Stock</p> {% endif %} </div>
JavaScript:
// components/product_card/product_card.js // Access component JS variables in $onComponent callback $onComponent(({ product_id, price, api_endpoint }, ctx) => { const containerEl = ctx.els[0]; containerEl.querySelector(".add-to-cart") .addEventListener("click", () => { fetch(api_endpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "add_to_cart", price: price }), }); }); });
CSS:
/* components/product_card/product_card.css */ /* Access component CSS variables */ .product-card { background-color: var(--card_bg); color: var(--text_color); border-radius: 8px; padding: 16px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .price { color: var(--price_color); font-weight: bold; }
Use the component like this:
{% component "product_card" product_id=123 theme="dark" show_price=True %} {% endcomponent %}
And this is what gets rendered:
<div class="product-card" data-product-id="123" data-djc-css-a1b2c3> <img src="/media/product.jpg" alt="Awesome Product" /> <h3>Awesome Product</h3> <p class="price">$29.99</p> <button class="add-to-cart">Add to Cart</button> </div>
Read on to learn about all the exciting details and configuration possibilities!
(If you instead prefer to jump right into the code, check out the example project)
Features
Modern and modular UI
- Create self-contained, reusable UI elements.
- Each component can include its own HTML, CSS, and JS, or additional third-party JS and CSS.
- HTML, CSS, and JS can be defined on the component class, or loaded from files.
from django_components import Component @register("calendar") class Calendar(Component): template = """ <div class="calendar"> Today's date is <span>{{ date }}</span> </div> """ css = """ .calendar { width: 200px; background: pink; } """ js = """ document.querySelector(".calendar") .addEventListener("click", () => { alert("Clicked calendar!"); }); """ # Additional JS and CSS class Media: js = ["https://cdn.jsdelivr.net/npm/htmx.org@2/dist/htmx.min.js"] css = ["bootstrap/dist/css/bootstrap.min.css"] # Variables available in the template def get_template_data(self, args, kwargs, slots, context): return { "date": kwargs["date"] }
Extended template tags
django-components is designed for flexibility, making working with templates a breeze.
It extends Django's template tags syntax with:
- Python expressions
disabled=(not editable)to evaluate Python code in templates - Literal lists and dictionaries
headers=["Name", "Age"]anddata=[{"name": "John"}]to pass structured data directly - Self-closing tags
{% mytag / %} - Multi-line template tags
- Spread operator
...to dynamically pass args or kwargs into the template tag - Nested templates like
"{{ first_name }} {{ last_name }}" - Flat dictionaries
dict:key=val
{% component "table" disabled=(not user.is_active) title="Friend list for {{ user.name }}" headers=["Name", "Age", "Email"] data=[ { "name": "Jane"|upper, "age": 25|add:1, "email": "jane@example.com", "hobbies": ["reading", "coding"], }, ], ...default_attrs attrs:class="py-4 ma-2 border-2 border-gray-300 rounded-md" / %}
You too can define template tags with these features by using
@template_tag()
or BaseNode.
Read more on Custom template tags.
Composition with slots
- Render components inside templates with
{% component %}tag. - Compose them with
{% slot %}and{% fill %}tags. - Vue-like slot system, including scoped slots.
{% component "Layout" bookmarks=bookmarks breadcrumbs=breadcrumbs %} {% fill "header" %} <div class="flex justify-between gap-x-12"> <div class="prose"> <h3>{{ project.name }}</h3> </div> <div class="font-semibold text-gray-500"> {{ project.start_date }} - {{ project.end_date }} </div> </div> {% endfill %} {# Access data passed to `{% slot %}` with `data` #} {% fill "tabs" data="tabs_data" %} {% component "TabItem" header="Project Info" %} {% component "ProjectInfo" project=project project_tags=project_tags attrs:class="py-5" attrs:width=tabs_data.width / %} {% endcomponent %} {% endfill %} {% endcomponent %}
Full programmatic access
When you render a component, you can access everything about the component:
- Component input: args, kwargs, slots and context
- Component's template, CSS and JS
- Django's context processors
- Unique render ID
class Table(Component): js_file = "table.js" css_file = "table.css" template = """ <div class="table"> <span>{{ variable }}</span> </div> """ def get_template_data(self, args, kwargs, slots, context): # Access component's ID assert self.id == "djc1A2b3c" # Access component's inputs and slots assert self.args == [123, "str"] assert self.kwargs == {"variable": "test", "another": 1} footer_slot = self.slots["footer"] some_var = self.context["some_var"] # 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" return { "variable": kwargs["variable"], } # Access component's HTML / JS / CSS Table.template Table.js Table.css # Render the component rendered = Table.render( kwargs={"variable": "test", "another": 1}, args=(123, "str"), slots={"footer": "MY_FOOTER"}, )
Granular HTML attributes
Use the {% html_attrs %} template tag to render HTML attributes.
It supports:
- Defining attributes as whole dictionaries or keyword arguments
- Merging attributes from multiple sources
- Boolean attributes
- Appending attributes
- Removing attributes
- Defining default attributes
<div {% html_attrs attrs defaults:class="default-class" class="extra-class" %} >
{% html_attrs %} offers a Vue-like granular control for
class
and style
HTML attributes,
where you can use a dictionary to manage each class name or style property separately.
{% html_attrs class="foo bar" class={ "baz": True, "foo": False, } class="extra" %}
{% html_attrs style="text-align: center; background-color: blue;" style={ "background-color": "green", "color": None, "width": False, } style="position: absolute; height: 12px;" %}
Read more about HTML attributes.
HTML fragment support
django-components makes integration with HTMX, AlpineJS or jQuery easy by allowing components to be rendered as HTML fragments:
-
Components's JS and CSS files are loaded automatically when the fragment is inserted into the DOM.
-
Components can be exposed as Django Views with
get(),post(),put(),patch(),delete()methods -
Automatically create an endpoint for a component with
Component.View.public
# components/calendar/calendar.py @register("calendar") class Calendar(Component): template_file = "calendar.html" class View: # Define handlers def get(self, request, *args, **kwargs): page = request.GET.get("page", 1) return Calendar.render_to_response( request=request, kwargs={ "page": page, }, ) def get_template_data(self, args, kwargs, slots, context): return { "page": kwargs["page"], } # Get auto-generated URL for the component url = get_component_url(Calendar) # Or define explicit URL in urls.py path("calendar/", Calendar.as_view())
Provide / Inject
django-components supports the provide / inject pattern, similarly to React's Context Providers or Vue's provide / inject:
- Use the
{% provide %}tag to provide data to the component tree - Use the
Component.inject()method to inject data into the component
Read more about Provide / Inject.
<body> {% provide "theme" variant="light" %} {% component "header" / %} {% endprovide %} </body>
@register("header")
class Header(Component):
template = "..."
def get_template_data(self, args, kwargs, slots, context):
theme = self.inject("theme").variant
return {
"theme": theme,
}
Input validation and static type hints
Avoid needless errors with type hints and runtime input validation.
To opt-in to input validation, define types for component's args, kwargs, slots, and more:
from django.template import Context from django_components import Component, Slot, SlotInput class Button(Component): class Args: size: int text: str class Kwargs: variable: str another: int maybe_var: int | None = None # May be omitted class Slots: my_slot: SlotInput | None = None another_slot: SlotInput def get_template_data(self, args: Args, kwargs: Kwargs, slots: Slots, context: Context): args.size # int kwargs.variable # str slots.my_slot # Slot[MySlotData]
To have type hints when calling
Button.render() or
Button.render_to_response(),
wrap the inputs in their respective Args, Kwargs, and Slots classes:
Button.render( # Error: First arg must be `int`, got `float` args=Button.Args( size=1.25, text="abc", ), # Error: Key "another" is missing kwargs=Button.Kwargs( variable="text", ), )
Extensions
Django-components functionality can be extended with Extensions. Extensions allow for powerful customization and integrations. They can:
- Tap into lifecycle events, such as when a component is created, deleted, or registered
- Add new attributes and methods to the components
- Add custom CLI commands
- Add custom URLs
Some of the extensions include:
- Component caching
- Django View integration
- Component defaults
- Pydantic integration (input validation)
Some of the planned extensions include:
- AlpineJS integration
- Storybook integration
- Component-level benchmarking with asv
Caching
- Components can be cached using Django's cache framework.
- Caching rules can be configured on a per-component basis.
- Components are cached based on their input. Or you can write custom caching logic.
from django_components import Component class MyComponent(Component): class Cache: enabled = True ttl = 60 * 60 * 24 # 1 day def hash(self, *args, **kwargs): return hash(f"{json.dumps(args)}:{json.dumps(kwargs)}")
Simple testing
- Write tests for components with
@djc_testdecorator. - The decorator manages global state, ensuring that tests don't leak.
- If using
pytest, the decorator allows you to parametrize Django or Components settings. - The decorator also serves as a stand-in for Django's
@override_settings.
from django_components.testing import djc_test from components.my_table import MyTable @djc_test def test_my_table(): rendered = MyTable.render( kwargs={ "title": "My table", }, ) assert rendered == "<table>My table</table>"
Debugging features
- Visual component inspection: Highlight components and slots directly in your browser.
- Detailed tracing logs to supply AI-agents with context: The logs include component and slot names and IDs, and their position in the tree.
Sharing components
-
Install and use third-party components from PyPI
-
Or publish your own "component registry"
-
Highly customizable - Choose how the components are called in the template (and more):
{% component "calendar" date="2024-11-06" %} {% endcomponent %} {% calendar date="2024-11-06" %} {% endcalendar %}
Documentation
Read the full documentation here.
... or jump right into the code, check out the example project.
Performance
Our aim is to be at least as fast as Django templates.
As of 0.130, django-components is ~4x slower than Django templates.
| Render time | |
|---|---|
| django | 68.9±0.6ms |
| django-components | 259±4ms |
See the full performance breakdown for more information.
Release notes
Read the Release Notes to see the latest features and fixes.
Community examples
One of our goals with django-components is to make it easy to share components between projects. If you have a set of components that you think would be useful to others, please open a pull request to add them to the list below.
-
django-htmx-components: A set of components for use with htmx.
-
djc-heroicons: A component that renders icons from Heroicons.com.
Contributing and development
Get involved or sponsor this project - See here
Running django-components locally for development - See here

