Create simple reusable template components in Django. https://django-components.github.io/django-components
Find a file
Juro Oravec 7dfcb447c4
feat: add decorator for writing component tests (#1008)
* feat: add decorator for writing component tests

* refactor: udpate changelog + update deps pins

* refactor: fix deps

* refactor: make cached_ref into generic and fix linter errors

* refactor: fix coverage testing

* refactor: use global var instead of env var for is_testing state
2025-03-02 19:46:12 +01:00
.github feat: benchmarking (#999) 2025-02-23 16:18:57 +01:00
benchmarks feat: benchmarking (#999) 2025-02-23 16:18:57 +01:00
docs feat: add decorator for writing component tests (#1008) 2025-03-02 19:46:12 +01:00
logo Update marks. 2025-01-22 23:51:12 +01:00
sampleproject refactor: fix component recursion (#936) 2025-02-01 17:19:21 +01:00
scripts docs: Move docs-folder to root (#816) 2024-12-03 12:32:21 +01:00
src feat: add decorator for writing component tests (#1008) 2025-03-02 19:46:12 +01:00
tests feat: add decorator for writing component tests (#1008) 2025-03-02 19:46:12 +01:00
.gitignore fix: TemplateDoesNotExist when using {% extends %} on main template and two components with same parent template (#862) 2024-12-23 12:58:03 +01:00
.pre-commit-config.yaml [pre-commit.ci] pre-commit autoupdate (#700) 2024-10-08 08:46:43 +02:00
asv.conf.json feat: benchmarking (#999) 2025-02-23 16:18:57 +01:00
CHANGELOG.md feat: add decorator for writing component tests (#1008) 2025-03-02 19:46:12 +01:00
CODE_OF_CONDUCT.md Update CODE_OF_CONDUCT.md 2024-01-17 09:40:07 +01:00
LICENSE Create LICENSE 2019-11-28 21:38:10 +01:00
MANIFEST.in feat: add JS dependency manager (#666) 2024-09-22 16:42:41 +02:00
mkdocs.yml refactor: use .nav.yml to define page order instead of nav weights (#1000) 2025-02-23 22:44:12 +01:00
pyproject.toml feat: add decorator for writing component tests (#1008) 2025-03-02 19:46:12 +01:00
README.md docs: add perf section (#1002) 2025-03-01 12:57:50 +01:00
requirements-ci.in feat: add decorator for writing component tests (#1008) 2025-03-02 19:46:12 +01:00
requirements-ci.txt feat: add decorator for writing component tests (#1008) 2025-03-02 19:46:12 +01:00
requirements-dev.in feat: add decorator for writing component tests (#1008) 2025-03-02 19:46:12 +01:00
requirements-dev.txt feat: add decorator for writing component tests (#1008) 2025-03-02 19:46:12 +01:00
requirements-docs.txt refactor: use .nav.yml to define page order instead of nav weights (#1000) 2025-02-23 22:44:12 +01:00
tox.ini feat: add decorator for writing component tests (#1008) 2025-03-02 19:46:12 +01:00

django-components

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

Read the full documentation

⚠️ Attention ⚠️ - We migrated from EmilStenstrom/django-components to django-components/django-components.

Repo name and documentation URL changed. Package name remains the same.

Report any broken links links in #922.

django-components 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.

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.html
from django_components import Component

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

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

    def get_context_data(self, date):
        return {"date": 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.1.1/dist/htmx.min.js"]
        css = ["bootstrap/dist/css/bootstrap.min.css"]

    # Variables available in the template
    def get_context_data(self, date):
        return {
            "date": date
        }

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

Extended template tags

django-components extends Django's template tags syntax with:

  • Literal lists and dictionaries in template tags
  • Self-closing tags {% mytag / %}
  • Multi-line template tags
  • Spread operator ... to dynamically pass args or kwargs into the template tag
  • Nested template tags like "{{ first_name }} {{ last_name }}"
  • Flat definition of dictionary keys attr:key=val
{% 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"
/ %}

HTML fragment support

django-components makes intergration with HTMX, AlpineJS or jQuery easy by allowing components to be rendered as HTML fragments:

  • Components's JS and CSS is loaded automatically when the fragment is inserted into the DOM.

  • Expose components as views with get, post, put, patch, delete methods

# components/calendar/calendar.py
@register("calendar")
class Calendar(Component):
    template_file = "calendar.html"

    def get(self, request, *args, **kwargs):
        page = request.GET.get("page", 1)
        return self.render_to_response(
            kwargs={
                "page": page,
            }
        )

    def get_context_data(self, page):
        return {
            "page": page,
        }

# urls.py
path("calendar/", Calendar.as_view()),

Type hints

Opt-in to type hints by defining types for component's args, kwargs, slots, and more:

from typing import NotRequired, Tuple, TypedDict, SlotContent, SlotFunc

ButtonArgs = Tuple[int, str]

class ButtonKwargs(TypedDict):
    variable: str
    another: int
    maybe_var: NotRequired[int] # May be omitted

class ButtonData(TypedDict):
    variable: str

class ButtonSlots(TypedDict):
    my_slot: NotRequired[SlotFunc]
    another_slot: SlotContent

ButtonType = Component[ButtonArgs, ButtonKwargs, ButtonSlots, ButtonData, JsData, CssData]

class Button(ButtonType):
    def get_context_data(self, *args, **kwargs):
        self.input.args[0]  # int
        self.input.kwargs["variable"]  # str
        self.input.slots["my_slot"]  # SlotFunc[MySlotData]

        return {}  # Error: Key "variable" is missing

When you then call Button.render() or Button.render_to_response(), you will get type hints:

Button.render(
    # Error: First arg must be `int`, got `float`
    args=(1.25, "abc"),
    # Error: Key "another" is missing
    kwargs={
        "variable": "text",
    },
)

Handle large projects with ease

  • Components can be infinitely nested.
  • (Soon) Optimize performance with component-level caching

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

Other features

  • Vue-like provide / inject system
  • Format HTML attributes with {% html_attrs %}

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