Django, live.
Production-ready.
Build reactive, real-time applications with Django and Python. djust 1.0 is here: a Rust VDOM for performance, resilient WebSocket recovery, accessibility in the box, and far less surface area for teams that want to ship without a JavaScript app.
djust 1.0 is here.
djust has reached a stable 1.0 contract — the public API is frozen and SemVer-stable after eighteen release candidates of hardening in production deployments. The story is unchanged: keep Django as the application model, use Rust where performance matters, and remove the frontend app unless you actually need one.
- Framework package consolidation has shipped: auth, tenants, theming, components, and admin all live behind core extras.
- Rust partial rendering, keyed loops, temporary assigns, streaming, server actions, and markdown streaming are all in the 1.0 line.
- The 1.0 release cycle was intentionally boring: accessibility, recovery polish, docs, and migration sharp edges — eighteen release candidates of it.
- Real-time community: GitHub for async, Discord for live chat — both linked in the footer.
API surface frozen and SemVer-stable. Shipped after eighteen release candidates of hardening in production deployments since the v0.9 series.
Server-render Django templates, then let Rust diff and patch only what changed.
No frontend build pipeline, duplicated model types, or API layer just to make a form interactive.
Production state backends, reconnect recovery, and HTTP fallback for real deployments.
Complexity is the enemy.
djust exists for teams that want interactive software without splitting their product into two applications. Keep the domain model, forms, validation, permissions, and rendering in Django. Use Rust for the VDOM hot path. Add JavaScript only when it earns its keep.
The goal for 1.0 is not novelty. It is a smaller, boringly reliable contract for server-rendered apps: fewer moving pieces, fewer duplicated types, fewer client/server seams, and a path from static HTML to real-time collaboration that still feels like Django.
Read the full philosophyNo mandatory API layer, frontend store, schema duplication, or build pipeline just to make ordinary product workflows feel live.
Start with Django templates. Layer in events, streaming, presence, optimistic updates, and server push where the interaction actually needs them.
Rust handles diffing and patch generation so Python can stay focused on the product logic your team already understands.
How djust works
Server-rendered reactivity in plain HTML — no API, no client state.
from djust import LiveView from djust.decorators import event_handler class CounterView(LiveView): template_name = 'counter.html' def mount(self, request, **kwargs): self.count = 0 # state @event_handler() def increment(self, **kwargs): self.count += 1 # re-renders
{% load djust_tags %} <body dj-view="{{ dj_view_id }}"> {% djust_scripts %} <div dj-root> <!-- reactive region --> <h1>{{ count }}</h1> <button dj-click="increment">+</button> </div> </body>
That's the whole loop. Click fires dj-click over a WebSocket, the handler mutates self.count, djust diffs the template in Rust, and only the changed bytes are patched into the page.
The directive vocabulary
Wire interactions with dj-* attributes right in your HTML. These 58 directives are one tab of a full API reference — alongside decorators, the view API, mixins, and config.
Events
dj-clickFire a handler on click. data-* attrs become kwargs.dj-submitSubmit a form. Named fields arrive as handler kwargs.dj-changeFire on blur / select change with the current value.dj-inputFire on every keystroke. Handler gets value=.dj-blurFire when focus leaves the element.dj-focusFire when focus enters the element.dj-click-awayFire when user clicks outside this element.dj-confirmShow a native confirm dialog before the event is sent.dj-modelTwo-way binding — auto-syncs self.field_name.dj-copyClient-only clipboard copy. No server round-trip.Keyboard
dj-keydownKeydown event. Supports .enter / .escape / .space modifiers.dj-keyupKeyup event. Same modifier support as dj-keydown.dj-shortcutBind keyboard chords (ctrl+k, escape, shift+enter).dj-window-keydownwindow keydown. Modifier filtering supported.dj-window-keyupwindow keyup.dj-window-scrollwindow scroll (150ms throttle).dj-window-clickwindow click (catches background clicks).dj-window-resizewindow resize (150ms throttle).dj-document-keydowndocument keydown (capture-phase).dj-document-keyupdocument keyup.dj-document-clickdocument click (capture-phase).Navigation
Loading states
Submit protection
UI feedback
dj-cloakHide elements until the connection is live (no FOUC).dj-scroll-into-viewAuto-scroll element into view after render (one-shot).dj-copy-feedbackButton text shown for 2s after dj-copy success.dj-copy-classCSS class added for 2s after dj-copy success.dj-copy-eventServer event fired after successful dj-copy.dj-dialogDrive a native <dialog> open/close declaratively.Lifecycle
VDOM identity
dj-viewOn <body> — identifies the WebSocket session. Required.dj-rootMarks the reactive subtree. Only HTML inside is diffed.dj-keyStable list-item identity for optimal VDOM diffing.data-keySame as dj-key, no prefix. Use whichever you prefer.dj-updateOpt out of patching. Use for charts / rich text / external JS.dj-hookBind a JS lifecycle hook (mounted/updated/destroyed).dj-mutationFire a server event when the DOM mutates (MutationObserver).Data passing
Animation & transitions
Streaming & big lists
Now try it in your browser.
start.djust.org is a real, multi-user djust app — click reactions, vote in a live poll, and post to a guestbook, then watch it sync for everyone at once. The Python behind each interaction is shown right on the page, so you can read the loop you just saw above as it actually runs.
Open it in two tabs and watch them update in real time — that's djust's server push, presence, and Rust-diffed patching working together, with zero frontend build.
Start your own clones the djust-start repo — a running app in under 2 minutes.
Click and watch the count jump for everyone.
Vote and see results update in real time.
Sign it — new entries appear instantly.
State Management Primitives
Complex client-side behavior, declared in Python. djust provides a suite of decorators that handle the hard parts of frontend development for you.
Delay server requests until the user stops typing. Perfect for search inputs.
defsearch(self, query):
self.results = ...
Update the UI instantly, validate on the server later. Zero latency feel.
deflike(self):
self.liked = True
Cache responses client-side. Instant results for repeated queries.
defget_cities(self):
return Cities.all()
Sync multiple components instantly without a server roundtrip.
defswitch_tab(self, tab):
self.tab = tab
Copy. Paste. Own.
Stop fighting with npm packages. djust uses a component-as-code philosophy. Copy our components into your project and customize them to your heart's content.
- Framework AgnosticSwitch between Bootstrap 5 and Tailwind CSS with a single config setting.
- Two-Tier ArchitectureUse lightweight
Componentfor static UI and powerfulLiveComponentfor interactive widgets.
class Navbar(Component): def render(self): framework = config.get('css_framework') if framework == 'bootstrap5': elif framework == 'tailwind': return self._render_tailwind() return self._render_plain()
The End of N+1 Queries.
The #1 performance killer in Django apps is the N+1 query problem. djust solves it automatically.
Our compiler analyzes your templates to see exactly which fields you use (e.g., {{ book.author.name }}). It then automatically injects the optimal select_related calls into your QuerySet.
{% for book in books %}
{{ book.author.name }}
{% endfor %}{% for book in books %}
{{ book.author.name }}
{% endfor %}JOIN authors ON ...
Everything Real-Time. Out of the Box.
Streaming, presence, server push, and data binding — all built into the framework with zero configuration.
Stream LLM tokens, logs, or any async data directly to the browser.
Track who is online with automatic join/leave detection.
Push updates from background tasks to connected clients.
Two-way form binding with zero event handlers.
Real-time Collaboration.
Three Lines of Python.
Presence tracking, live cursors, and broadcast updates — without WebSocket boilerplate, pub/sub clients, or frontend state machines.
classDocView(LiveView, PresenceMixin):
presence_key = "doc:{doc_id}"
defmount(self, request, **kwargs):
self.track_presence(
meta={"name": request.user.username}
)
defhandle_presence_join(self, presence):
# fires on every tab/device — auto-deduped
self.viewers = self.list_presences()
defsave_document(self):
self.doc.save()
self.broadcast_to_presence(
"doc_updated",
{"saved_by": self.request.user.username},
)
classDocView(LiveView, LiveCursorMixin):
...
defget_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["cursors"] = self.get_cursors()
return ctx
join/leave hooks, metadata, group keys — directly inspired by Phoenix Presence.
Phoenix requires JS to merge presence diffs client-side. djust handles it server-side — your template just renders presences.
Memory backend for dev, Redis for prod. Swap with one settings change.