Skip to main content

A way to create simple reusable template components in Django.

Project description

django-components

PyPI - Version PyPI - Python Version PyPI - License PyPI - Downloads GitHub Actions Workflow Status asvDiscord

Read the full documentation

See Roadmap for 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.

Sponsors

Quickstart

A component in django-components can be as simple as a Django template and Python code to declare the component:

{# components/calendar/calendar.html #}
<div class="calendar">
  Today's date is <span>{{ date }}</span>
</div>
# components/calendar/calendar.py
from django_components import Component, register

@register("calendar")
class Calendar(Component):
    template_file = "calendar.html"

Or a combination of Django template, Python, CSS, and Javascript:

{# components/calendar/calendar.html #}
<div class="calendar">
  Today's date is <span>{{ date }}</span>
</div>
/* components/calendar/calendar.css */
.calendar {
  width: 200px;
  background: pink;
}
/* components/calendar/calendar.js */
document.querySelector(".calendar").onclick = () => {
  alert("Clicked calendar!");
};
# components/calendar/calendar.py
from django_components import Component, register

@register("calendar")
class Calendar(Component):
    template_file = "calendar.html"
    js_file = "calendar.js"
    css_file = "calendar.css"

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

Use the component like this:

{% component "calendar" date="2024-11-06" %}{% endcomponent %}

And this is what gets rendered:

<div class="calendar-component">
  Today's date is <span>2024-11-06</span>
</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"]
        }

Composition with 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 %}

Extended template tags

django-components is designed for flexibility, making working with templates a breeze.

It extends Django's template tags syntax with:

{% component "table"
    ...default_attrs
    title="Friend list for {{ user.name }}"
    headers=["Name", "Age", "Email"]
    data=[
        {
            "name": "John"|upper,
            "age": 30|add:1,
            "email": "john@example.com",
            "hobbies": ["reading"],
        },
        {
            "name": "Jane"|upper,
            "age": 25|add:1,
            "email": "jane@example.com",
            "hobbies": ["reading", "coding"],
        },
    ],
    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.

Full programmatic access

When you render a component, you can access everything about the component:

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:

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 typing import Optional
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: Optional[int] = None  # May be omitted

    class Slots:
        my_slot: Optional[SlotInput] = 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:

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_test 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.
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.
Component debugging visualization showing slot highlighting

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.

Contributing and development

Get involved or sponsor this project - See here

Running django-components locally for development - See here

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_components-0.145.0.tar.gz (207.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_components-0.145.0-py3-none-any.whl (226.9 kB view details)

Uploaded Python 3

File details

Details for the file django_components-0.145.0.tar.gz.

File metadata

  • Download URL: django_components-0.145.0.tar.gz
  • Upload date:
  • Size: 207.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for django_components-0.145.0.tar.gz
Algorithm Hash digest
SHA256 6a5655c6b69d5c802ed37f0b8497a1b32202e8965e3850a595a5eb20106539d9
MD5 50b19b8bae04d348c4058c3b6e30e0b8
BLAKE2b-256 f1ef82f4388e197de8b21a63c4c0399869e8c8e1d78ea81b6d64a30806b8017d

See more details on using hashes here.

File details

Details for the file django_components-0.145.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_components-0.145.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bf601dace522dd459e61859bba141dd66cf56a13247f334726894e9ac3933feb
MD5 e3b31b2bee5d7e0b15521519e4f9c497
BLAKE2b-256 e87a7a7e2a84c913e83f4695d9cfdcc6c0ab116a409c219470fc0b6e07332eeb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page