django-unicorn is a reactive component framework for Django that allows developers to build full-stack interactive applications without needing a separate frontend framework like React, Vue, or Angular. It seamlessly integrates with Django's templates and views, bridging the gap between the backend and frontend.
Philosophy:
- Full-Stack Django: Keep logic in Python.
- Convention over Configuration: Uses strict naming conventions to wire components together.
- No Build Step Required: Works with vanilla Django templates (though JS can be minified).
Unicorn works by synchronizing the state between the backend Python view and the frontend DOM.
-
Initial Render:
- The
{% unicorn 'component-name' %}template tag finds the matchingUnicornViewand template. - The view's public attributes are serialized into a JSON object.
- The template is rendered with these attributes in the context.
- The HTML and the initial JSON state are sent to the client.
- The
-
Client-Side Initialization:
unicorn.js(loaded via{% unicorn_scripts %}) behaves like a lightweight frontend framework.- It parses the DOM for
unicorn:oru:attributes. - It binds event listeners (e.g.,
click,input,blur) to these elements. - It initializes the component state from the embedded JSON.
-
Interaction & Updates:
- When a user interacts (e.g., clicks a button, types in an input):
unicorn.jscaptures the event.- It sends an AJAX request to the Django backend with the component ID, the current data, and the action to perform.
- Server-Side Processing:
- Django restores the
UnicornViewstate from the request data. - It executes the requested Python method (action).
- It re-renders the component's template with the updated state.
- Django restores the
- DOM Update:
- The server returns the new HTML and any updated data.
unicorn.jsreceives the response.- It uses
morphdomto intelligently diff and patch the DOM, updating only the changed elements without losing focus or scroll position.
- When a user interacts (e.g., clicks a button, types in an input):
Located in src/django_unicorn/components/.
- Inherits from
django_unicorn.components.UnicornView. - State: Public class attributes map directly to the frontend state.
- Actions: Methods explicitly callable from the template.
- Lifecycle Hooks:
mount(): Called on initialization.hydrate(): Called when data is restored from the frontend.updating(name, value)/updated(name, value): Called around property changes.
- Meta Options:
exclude: Attributes to keep on the server only.javascript_exclude: Attributes available in template but not serialized to JS.safe: Attributes valid to render HTML (XSS prevention).
Located in src/django_unicorn/static/js/.
- Handles connection to the backend.
- Parses
unicorn:modelfor two-way data binding. - Parses
unicorn:click,unicorn:change, etc., for event binding. - Manages the AJAX queue and DOM morphing.
{% unicorn_scripts %}: Injects the JavaScript library and initialization code.{% unicorn 'component-name' %}: Renders a component. Names are dash-cased (e.g.,hello-world) which maps tohello_world.HelloWorldView.
src/django_unicorn/: Core library code.components/: BaseUnicornViewlogic.views/: AJAX endpoint handlers.serializer.py: JSON serialization/deserialization logic.typer.py: Type hinting and casting helpers.call_method_parser.py: Parses string representations of method calls from templates.static/: Frontend JavaScript assets.
example/: A full Django project demonstrating usage.manage.py: Standard Django entry point.www/: Main app for the example project.unicorn/components/: Example components (hello_world.py, etc.).
docs/: Sphinx documentation.
The project uses just (a command runner) for common tasks.
- Run Example Server:
just runserver(runs on port 8080). - Testing:
- Python:
just test-python(usespytest). - JavaScript:
just test-js(usesnpmtests). - Full Matrix:
just test-python-matrix(usesactfor local GitHub Actions).
- Python:
- Linting:
just type(usesruffandty). - Documentation:
just docs-serve(builds and serves Sphinx docs).
- Mutable Default Arguments: A classic Python gotcha. Do not use
list = []ordict = {}as class attributes inUnicornView. Initialize them inmount()instead to avoid shared state across users. - Serialization:
django-unicornhandles standard types (str, int, float, Decimal, bool), Django Models/QuerySets, Dataclasses, and Pydantic models. Complex custom objects need explicit serialization logic. - Security: Attributes are HTML-encoded by default. Use
Meta.safeor thesafetemplate filter for raw HTML, but be wary of XSS. - Static Files: The JS files are bundled. Modifications to
src/django_unicorn/static/usually require runningnpm run build(viajust js-build). - Root Element: Components must have a single root element that is a valid wrapper (non-void element, e.g.
<div>,<span>,<section>, etc.). Void elements like<input>cannot be the root.
- Official Docs:
docs/folder or django-unicorn.com. - Contributing:
CONTRIBUTING.mdandDEVELOPING.mdfor setup instructions. - Example App:
example/directory contains many practical examples of components.