Lightweight reusable template components for Django. An exploratory fork of django-components.
Project description
django-components-lite
An exploratory, lightweight fork of django-components.
This package strips django-components down to its core: simple, reusable template components for Django, just templates with some optional Python logic. The goal is to see how a minimal django-components feels in practice.
Attribution
This project is built on the work of the django-components project by Emil Stenström, Juro Oravec, and all contributors.
If you're looking for a mature, full-featured, and widely used component library for Django, use django-components. It has an active community, extensive documentation, and a rich feature set.
How this compares
A few Django component libraries with different philosophies:
- django-components — the upstream project. Big, full-featured, introduces a lot of new template behavior, almost a parallel template language.
- django-cotton — HTML-like syntax (
<c-card title="..." />); template-only, no Python logic per component. - django-viewcomponent — modeled on Rails ViewComponent. One Python class per component encapsulating template + logic.
- slippers — intentionally tiny; template-only, no Python per component.
- JinjaX — HTML-like component syntax for Jinja2 (not Django templates).
django-components-lite sits on the small end of that spectrum: standard Django template tags ({% comp %} / {% slot %} / {% fill %}), one Python class per component for context logic, no special template syntax, no monkeypatches, no extension system.
If even this is more than you need, the package is small (~3000 LOC of regular Django patterns) and is a reasonable starting point to copy into your project and inline rather than depend on as a separate package.
Features
What django-components-lite keeps:
- Component classes with Python logic and Django templates
{% comp %}/{% endcomp %}(and self-closing{% compc %}) template tags- Slots and fills (
{% slot %},{% fill %}) - Component autodiscovery
- Component registry
- Static file handling (JS/CSS)
- Isolated component context
- HTML attribute rendering utilities
What's removed (vs. upstream)
- Extension system
- Built-in components (
DynamicComponent,ErrorFallback) - Component caching
- Provide/Inject system
- Template expressions
- Management commands
- JS/CSS data methods and dependency management
- Type validation (Args/Kwargs/Slots/TemplateData)
on_render()generator system and deferred renderingcontext_behaviorsetting (always isolated, like Django'sinclusion_tag)- Tag formatters
- Component views and URLs
librariessetting andimport_libraries()reload_on_file_changesetting- All deprecated setting aliases
Installation
pip install django-components-lite
Add to INSTALLED_APPS:
INSTALLED_APPS = [
# ...
"django_components_lite",
]
Add the component template loader so component templates get discovered:
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"OPTIONS": {
"loaders": [
"django.template.loaders.filesystem.Loader",
"django.template.loaders.app_directories.Loader",
"django_components_lite.template_loader.Loader",
],
},
},
]
By default, components are discovered in:
- a
components/directory at project root, and - a
components/directory inside each installed app.
To customize, set COMPONENTS:
from django_components_lite import ComponentsSettings
COMPONENTS = ComponentsSettings(
dirs=[BASE_DIR / "components"],
app_dirs=["components"],
)
Defining a component
A component is a Python class with a template:
# components/greeting/greeting.py
from django_components_lite import Component, register
@register("greeting")
class Greeting(Component):
template_file = "greeting.html"
def get_context_data(self, name="World"):
return {"name": name}
<!-- components/greeting/greeting.html -->
{% load component_tags %}
<div class="greeting">
Hello, {{ name }}!
{% slot "extra" %}{% endslot %}
</div>
template_file is resolved relative to the component's Python file, then relative to COMPONENTS.dirs, then Django's template dirs. You can use template = "..." for an inline template string instead.
To attach static files, place them next to the component and declare them on the class:
components/greeting/
greeting.py
greeting.html
greeting.css
greeting.js
class Greeting(Component):
template_file = "greeting.html"
css_file = "greeting.css"
js_file = "greeting.js"
When the component renders, <link> and <script> tags for the declared files are prepended to the output.
Using a component
From a template:
{% load component_tags %}
{% comp "greeting" name="Django" %}
{% fill "extra" %}<p>Welcome!</p>{% endfill %}
{% endcomp %}
Self-closing form (no body / no fills):
{% compc "greeting" name="Django" / %}
Positional arguments are routed to named parameters on get_context_data:
def get_context_data(self, title, body=""): ...
{% comp "card" "My Title" "Body text" %}{% endcomp %}
binds title="My Title" and body="Body text". Mixed positional + keyword args follow Python call semantics — passing the same parameter both ways raises TypeError. If your override declares *args, positional tag args are forwarded as args.
From Python:
html = Greeting.render(kwargs={"name": "Django"})
Component.render_to_response(...) is also available and returns an HttpResponse.
Slots
Slots let parent templates inject content into specific spots in a component.
{% load component_tags %}
<div class="panel">
<header>{% slot "header" %}Default header{% endslot %}</header>
<main>{% slot "body" required %}{% endslot %}</main>
<footer>{% slot "footer" %}{% endslot %}</footer>
</div>
Fill them with {% fill %}:
{% comp "panel" %}
{% fill "header" %}<h2>Custom Header</h2>{% endfill %}
{% fill "body" %}<p>Panel content.</p>{% endfill %}
{% endcomp %}
Content placed directly inside {% comp %} (no {% fill %}) goes into the slot marked default:
{% slot "content" default %}{% endslot %}
{% comp "panel" %}
This goes into the default slot.
{% endcomp %}
The body of {% slot %} is the fallback, used when no {% fill %} is provided. To branch on whether a slot was filled, check self.slots in Python and pass the result as a context variable:
def get_context_data(self, **kwargs):
return {"has_header": "header" in self.slots}
Settings
from django_components_lite import ComponentsSettings
COMPONENTS = ComponentsSettings(
autodiscover=True,
dirs=[BASE_DIR / "components"],
app_dirs=["components"],
static_files_allowed=[".css", ".js"],
static_files_forbidden=[".html", ".py"],
)
| Setting | Default | Description |
|---|---|---|
autodiscover |
True |
Automatically discover components in app directories |
dirs |
[BASE_DIR / "components"] |
Root-level directories to search for components |
app_dirs |
["components"] |
Subdirectory name within apps to search for components |
static_files_allowed |
CSS, JS, images, fonts | File extensions served as static files |
static_files_forbidden |
.html, .py, etc. |
File extensions never served as static files |
Component tag names are fixed: {% comp %} / {% endcomp %} / {% compc %}. They are not configurable.
API reference
Component
Subclass to define your own component.
Class attributes:
template_file— Path to the template. Resolved relative to the component's Python file, thenCOMPONENTS.dirs, then Django template dirs.template— Inline template string (alternative totemplate_file).template_name— Legacy alias fortemplate_file.css_file— Path to a CSS file. Its<link>is prepended to rendered output.js_file— Path to a JS file. Its<script>is prepended.
Instance attributes (available in get_context_data):
self.args— positional arguments passed to the component.self.kwargs— keyword arguments.self.slots— dict of slot name toSlotinstance."name" in self.slotschecks whether a slot was filled.self.context— outer DjangoContextat the call site.self.request— theHttpRequestif available, elseNone.
Methods:
get_context_data(**kwargs)— return a dict of context variables. Override with any signature.Component.render(args=None, kwargs=None, slots=None, context=None, request=None)— class method, returns rendered HTML string.Component.render_to_response(...)— class method, returnsHttpResponse. Same arguments asrender().
Registration
from django_components_lite import register, registry
@register("name")
class MyComponent(Component): ...
# or manually:
registry.register("name", MyComponent)
registry.unregister("name")
registry.get("name") # component class
registry.all() # dict of all registered components
Template tags
Available after {% load component_tags %}:
| Tag | Description |
|---|---|
{% comp "name" %}...{% endcomp %} |
Render a component, with optional slot fills in the body |
{% compc "name" / %} |
Self-closing form, no body |
{% slot "name" %}...{% endslot %} |
Define a slot in a component template |
{% fill "name" %}...{% endfill %} |
Fill a slot when using a component |
{% html_attrs attrs defaults key=val %} |
Render an HTML attribute string by merging attrs over defaults, then appending extra kwargs (class/style are space-joined) |
HTML attribute helpers
For composing attribute dicts in Python (used by {% html_attrs %} under the hood):
from django_components_lite import format_attributes, merge_attributes
merge_attributes({"class": "btn"}, {"class": "btn-primary"})
# -> {"class": "btn btn-primary"}
format_attributes({"class": "btn", "disabled": True})
# -> 'class="btn" disabled'
Changelog
See CHANGELOG.md.
Links
- django-components (upstream) — the full-featured upstream project
- Issues
License
MIT — see LICENSE.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_components_lite-0.5.2.tar.gz.
File metadata
- Download URL: django_components_lite-0.5.2.tar.gz
- Upload date:
- Size: 72.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3980328caac4f2e6e7131c0e6cd376db25d596e2730d426713691c26d1ed3e57
|
|
| MD5 |
dd1206cd5c4dc681a8e8b173dca5f7a0
|
|
| BLAKE2b-256 |
2e8fb8823885783669eb6feec47c6054cc1d8e5f82c6cc7d4f3f504abfa8882f
|
Provenance
The following attestation bundles were made for django_components_lite-0.5.2.tar.gz:
Publisher:
publish.yml on oliverhaas/django-components-lite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_components_lite-0.5.2.tar.gz -
Subject digest:
3980328caac4f2e6e7131c0e6cd376db25d596e2730d426713691c26d1ed3e57 - Sigstore transparency entry: 1419377596
- Sigstore integration time:
-
Permalink:
oliverhaas/django-components-lite@4111b7ed673fa1206a648a7eb7747e69b5a66fbc -
Branch / Tag:
refs/heads/main - Owner: https://github.com/oliverhaas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4111b7ed673fa1206a648a7eb7747e69b5a66fbc -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file django_components_lite-0.5.2-py3-none-any.whl.
File metadata
- Download URL: django_components_lite-0.5.2-py3-none-any.whl
- Upload date:
- Size: 48.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ebd84ccb96bf813f96d8713508d54a5cd09f646c998c2a99f8bc982efa038b9
|
|
| MD5 |
b289e906811b57c69e97e70091e90111
|
|
| BLAKE2b-256 |
c3e7d6bbc39a47d3c4d52a52035906d931d33be717dd5ecfd5e48d17d984d4d8
|
Provenance
The following attestation bundles were made for django_components_lite-0.5.2-py3-none-any.whl:
Publisher:
publish.yml on oliverhaas/django-components-lite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_components_lite-0.5.2-py3-none-any.whl -
Subject digest:
1ebd84ccb96bf813f96d8713508d54a5cd09f646c998c2a99f8bc982efa038b9 - Sigstore transparency entry: 1419377718
- Sigstore integration time:
-
Permalink:
oliverhaas/django-components-lite@4111b7ed673fa1206a648a7eb7747e69b5a66fbc -
Branch / Tag:
refs/heads/main - Owner: https://github.com/oliverhaas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4111b7ed673fa1206a648a7eb7747e69b5a66fbc -
Trigger Event:
workflow_dispatch
-
Statement type: