https://github.com/django-components/django-components
Create simple reusable template components in Django.
https://github.com/django-components/django-components
components django django-templates pypi-package reusable-components
Last synced: 2 months ago
JSON representation
Create simple reusable template components in Django.
- Host: GitHub
- URL: https://github.com/django-components/django-components
- Owner: django-components
- License: mit
- Created: 2015-06-11T17:23:48.000Z (almost 11 years ago)
- Default Branch: master
- Last Pushed: 2025-05-13T09:53:37.000Z (11 months ago)
- Last Synced: 2025-05-13T13:12:06.962Z (11 months ago)
- Topics: components, django, django-templates, pypi-package, reusable-components
- Language: Python
- Homepage: https://django-components.github.io/django-components
- Size: 24.4 MB
- Stars: 1,343
- Watchers: 16
- Forks: 94
- Open Issues: 52
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# 
[](https://pypi.org/project/django-components/) [](https://pypi.org/project/django-components/) [](https://github.com/django-components/django-components/blob/master/LICENSE/) [](https://pypistats.org/packages/django-components) [](https://github.com/django-components/django-components/actions/workflows/tests.yml) [](https://django-components.github.io/django-components/latest/benchmarks/)[](https://discord.gg/NaQ8QPyHtD)
### [Read the full documentation](https://django-components.github.io/django-components/latest/)
### [See Roadmap for v1](https://github.com/orgs/django-components/projects/1/views/1?sliceBy%5Bvalue%5D=milestone--v1)
`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:
```python
# 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:
```htmldjango
{# templates/product_card/product_card.html #}
{{ product.name }}
{% if show_price %}
${{ product.price }}
{% endif %}
{% if is_in_stock %}
Add to Cart
{% else %}
Out of Stock
{% endif %}
```
JavaScript:
```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:
```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:
```django
{% component "product_card"
product_id=123
theme="dark"
show_price=True
%}
{% endcomponent %}
```
And this is what gets rendered:
```html
Awesome Product
$29.99
Add to Cart
```
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](https://github.com/django-components/django-components/tree/master/sampleproject))
## 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.
```python
from django_components import Component
@register("calendar")
class Calendar(Component):
template = """
Today's date is
{{ date }}
"""
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](https://django-components.github.io/django-components/latest/concepts/fundamentals/template_tag_syntax#python-expressions) `disabled=(not editable)` to evaluate Python code in templates
- [Literal lists and dictionaries](https://django-components.github.io/django-components/latest/concepts/fundamentals/template_tag_syntax#literal-lists-and-dictionaries) `headers=["Name", "Age"]` and `data=[{"name": "John"}]` to pass structured data directly
- [Self-closing tags](https://django-components.github.io/django-components/latest/concepts/fundamentals/template_tag_syntax#self-closing-tags) `{% mytag / %}`
- [Multi-line template tags](https://django-components.github.io/django-components/latest/concepts/fundamentals/template_tag_syntax#multiline-tags)
- [Spread operator](https://django-components.github.io/django-components/latest/concepts/fundamentals/template_tag_syntax#spread-operator) `...` to dynamically pass args or kwargs into the template tag
- [Nested templates](https://django-components.github.io/django-components/latest/concepts/fundamentals/template_tag_syntax#nested-templates) like `"{{ first_name }} {{ last_name }}"`
- [Flat dictionaries](https://django-components.github.io/django-components/latest/concepts/fundamentals/template_tag_syntax#flat-dictionaries) `dict:key=val`
```django
{% 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()`](https://django-components.github.io/django-components/latest/reference/api/#django_components.template_tag)
or [`BaseNode`](https://django-components.github.io/django-components/latest/reference/api/#django_components.BaseNode).
Read more on [Custom template tags](https://django-components.github.io/django-components/latest/concepts/advanced/template_tags/).
### Composition with slots
- Render components inside templates with
[`{% component %}`](https://django-components.github.io/django-components/latest/reference/template_tags#component) tag.
- Compose them with [`{% slot %}`](https://django-components.github.io/django-components/latest/reference/template_tags#slot)
and [`{% fill %}`](https://django-components.github.io/django-components/latest/reference/template_tags#fill) tags.
- Vue-like slot system, including [scoped slots](https://django-components.github.io/django-components/latest/concepts/fundamentals/slots/#slot-data).
```django
{% component "Layout"
bookmarks=bookmarks
breadcrumbs=breadcrumbs
%}
{% fill "header" %}
{{ project.name }}
{{ project.start_date }} - {{ project.end_date }}
{% 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](https://django-components.github.io/django-components/latest/concepts/fundamentals/render_api/#component-inputs)
- Component's template, CSS and JS
- Django's [context processors](https://django-components.github.io/django-components/latest/concepts/fundamentals/render_api/#request-and-context-processors)
- Unique [render ID](https://django-components.github.io/django-components/latest/concepts/fundamentals/render_api/#component-id)
```python
class Table(Component):
js_file = "table.js"
css_file = "table.css"
template = """
{{ variable }}
"""
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 %}`](https://django-components.github.io/django-components/latest/concepts/fundamentals/html_attributes/) 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
```django
```
[`{% html_attrs %}`](https://django-components.github.io/django-components/latest/concepts/fundamentals/html_attributes/) offers a Vue-like granular control for
[`class`](https://django-components.github.io/django-components/latest/concepts/fundamentals/html_attributes/#merging-class-attributes)
and [`style`](https://django-components.github.io/django-components/latest/concepts/fundamentals/html_attributes/#merging-style-attributes)
HTML attributes,
where you can use a dictionary to manage each class name or style property separately.
```django
{% html_attrs
class="foo bar"
class={
"baz": True,
"foo": False,
}
class="extra"
%}
```
```django
{% 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](https://django-components.github.io/django-components/latest/concepts/fundamentals/html_attributes/).
### HTML fragment support
`django-components` makes integration with HTMX, AlpineJS or jQuery easy by allowing components to be rendered as [HTML fragments](https://django-components.github.io/django-components/latest/concepts/advanced/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](https://django-components.github.io/django-components/latest/concepts/fundamentals/component_views_urls/) with `get()`, `post()`, `put()`, `patch()`, `delete()` methods
- Automatically create an endpoint for a component with [`Component.View.public`](https://django-components.github.io/django-components/latest/concepts/fundamentals/component_views_urls/#register-urls-automatically)
```py
# 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](https://react.dev/learn/passing-data-deeply-with-context) or Vue's [provide / inject](https://vuejs.org/guide/components/provide-inject):
- Use the [`{% provide %}`](https://django-components.github.io/django-components/latest/reference/template_tags/#provide) tag to provide data to the component tree
- Use the [`Component.inject()`](https://django-components.github.io/django-components/latest/reference/api/#django_components.Component.inject) method to inject data into the component
Read more about [Provide / Inject](https://django-components.github.io/django-components/latest/concepts/advanced/provide_inject).
```django
{% provide "theme" variant="light" %}
{% component "header" / %}
{% endprovide %}
```
```djc_py
@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](https://django-components.github.io/django-components/latest/concepts/fundamentals/typing_and_validation/).
To opt-in to input validation, define types for component's args, kwargs, slots, and more:
```py
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()`](https://django-components.github.io/django-components/latest/reference/api/#django_components.Component.render) or
[`Button.render_to_response()`](https://django-components.github.io/django-components/latest/reference/api/#django_components.Component.render_to_response),
wrap the inputs in their respective `Args`, `Kwargs`, and `Slots` classes:
```py
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](https://django-components.github.io/django-components/latest/concepts/advanced/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](https://github.com/django-components/django-components/blob/master/src/django_components/extensions/cache.py)
- [Django View integration](https://github.com/django-components/django-components/blob/master/src/django_components/extensions/view.py)
- [Component defaults](https://github.com/django-components/django-components/blob/master/src/django_components/extensions/defaults.py)
- [Pydantic integration (input validation)](https://github.com/django-components/djc-ext-pydantic)
Some of the planned extensions include:
- AlpineJS integration
- Storybook integration
- Component-level benchmarking with asv
### Caching
- [Components can be cached](https://django-components.github.io/django-components/latest/concepts/advanced/component_caching/) 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.
```py
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_test`](https://django-components.github.io/django-components/latest/concepts/advanced/testing/) decorator.
- 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`](https://docs.djangoproject.com/en/5.2/topics/testing/tools/#django.test.override_settings).
```python
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 == "My 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):
```django
{% component "calendar" date="2024-11-06" %}
{% endcomponent %}
{% calendar date="2024-11-06" %}
{% endcalendar %}
```
## Documentation
[Read the full documentation here](https://django-components.github.io/django-components/latest/).
... or jump right into the code, [check out the example project](https://github.com/django-components/django-components/tree/master/sampleproject).
## 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](https://django-components.github.io/django-components/latest/benchmarks/) for more information.
## Release notes
Read the [Release Notes](https://github.com/django-components/django-components/tree/master/CHANGELOG.md)
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](https://github.com/iwanalabs/django-htmx-components): A set of components for use with [htmx](https://htmx.org/).
- [djc-heroicons](https://pypi.org/project/djc-heroicons/): A component that renders icons from [Heroicons.com](https://heroicons.com/).
## Contributing and development
Get involved or sponsor this project - [See here](https://django-components.github.io/django-components/dev/community/contributing/)
Running django-components locally for development - [See here](https://django-components.github.io/django-components/dev/community/development/)
