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. A representative dozen — there are ~40 in all.
Fire a handler on click. data-* attrs become kwargs.
Submit a form. Named fields arrive as handler kwargs.
Fire on every keystroke. Handler gets value=.
Fire on blur / select change with the current value.
Client-side navigation with history — no full reload.
Re-run a handler on an interval (default 5s).
Delay sending until the user pauses. Per element.
Two-way binding — auto-syncs self.field.
Auto class / show / hide / disable during a round-trip.
Native confirm dialog before the event is sent.
Hide elements until the connection is live (no FOUC).
Key events with modifiers like .enter / .escape.
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.