djust
Phoenix LiveView-style reactive server-side rendering for Django with Rust-powered performance
djust adds LiveView style reactivity to Django with a Rust core
djust lets Django developers write reactive, real time components in Python, diffing a Rust powered virtual DOM and patching over WebSocket.
What djust brings to Django
djust brings Phoenix LiveView style reactive components to Django. The README states the project is reactive server side rendering for Django, powered by Rust. You write server side Python, and the client updates automatically over a WebSocket. There is no JavaScript to write, no bundler, and no build step in your project, which is the core pitch: the reactive layer is a library you add rather than a front end toolchain you maintain.
The feature list expands on that. djust is described as fast because a Rust powered template engine and virtual DOM diffing run ten to one hundred times faster than plain Django rendering, with a link to a performance section. It offers reactive components in the LiveView style and stays Django compatible, working with existing Django templates and components. The client JavaScript is about 58 kilobytes gzipped, so no bundling is required. Updates travel over WebSocket as real time DOM patches, with an HTTP fallback. Diffing sends only what changed, which keeps payloads small. The Rust core handles templates, the virtual DOM, and parsing. A debug panel shows event history and virtual DOM inspection. Extra features include lazy hydration for below the fold content, TurboNav compatibility, progressive web app support with offline sync, multi tenant isolation, and view or handler level authorization through Django permissions.
How reactivity works
Reactivity rests on a Rust powered virtual DOM that diffs server rendered HTML and sends only the changed patches over WebSocket. A few template attributes do the wiring. The README shows a template that loads live_tags, emits client config in the head with a tag that auto injects the client runtime, marks the body with a dj-view attribute that connects the page to the WebSocket session, and wraps the reactive region in a div with dj-root. Static content outside dj-root is never touched by patching, so only the inner region is diffed.
For lists that reorder or change, the README advises adding data-key or dj-key to each item. With a key, djust emits a MoveChild patch instead of a remove then insert pair, which preserves DOM state such as focus, scroll position, and animations. Without a key it diffs by position, which is correct but causes more DOM mutations on reorder. The README also flags a common pitfall: a one sided if without an else inside an HTML attribute value can misalign virtual DOM patching because of branch aware div depth counting. The fix is to use a full if/else or move the conditional outside the tag. This caveat applies only to attribute values; if blocks in element content are fine. The full guidance lives in the linked VDOM architecture and template cheat sheet docs.
Getting started and the API
Getting a counter working takes five steps. Install with pip install djust django-channels. Add channels and djust to INSTALLED_APPS, set ASGI_APPLICATION, and configure an in memory channel layer. Replace asgi.py with a ProtocolTypeRouter that serves HTTP through Django and routes WebSocket paths such as ws/live/ to djust's LiveViewConsumer. Add a URL route pointing at the view. Then write a view class that subclasses LiveView, sets a template, initializes state in mount, and decorates methods with event_handler. Clicking a button updates the count with no page reload and no JavaScript written.
The API supports both class based and function based views. A class based view uses mount to set state and event_handler on methods that change it; a function based view uses the live_view decorator and returns locals as context. Templates use Django syntax plus event binding: dj-click for clicks, dj-input and dj-change for input and change events that pass the value, and dj-submit for form submission that passes form data as a dict. The 57 built in Django filters are supported, and djust's Rust template engine auto marks urlize, urlizetrunc, and unordered_list as safe so no manual safe filter is needed. A component system gives automatic, stable component IDs based on attribute names, so an AlertComponent assigned to self.alert_success gets the id alert_success and routes events back without manual string IDs.
Configuration, state, and navigation
Configuration lives in Django settings under LIVEVIEW_CONFIG. The main options are use_websocket, which defaults to true and requires Django Channels; debug_vdom, which enables detailed virtual DOM logging; strict_serialization, which can raise a TypeError for non serializable state and is recommended in development; and css_framework, which defaults to bootstrap5 and can be set to tailwind or none. A one command setup builds Tailwind CSS, and a minify flag does the same for production. Debug logging can be enabled in settings or programmatically through the config object.
State management adds Python only decorators so no JavaScript is needed. @debounce waits after typing stops, @throttle controls rapid events, @optimistic gives instant feedback, @cache stores responses, @client_state shares across components, and @background moves long work off the event loop. Navigation has three mechanisms: dj-patch and live_patch for filter, sort, or pagination within one view; dj-navigate and live_redirect for moving to a different LiveView on the same WebSocket; and a standard anchor for non LiveView pages. The README warns against using dj-click to call live_redirect, which adds a wasteful round trip, and instead recommends dj-navigate for direct links. Performance tables show template rendering speedups of 16.7x to 37.5x on an M1 MacBook Pro against plain Django.
Editorial conclusion
djust is published under the MIT license and requires Python 3.10 or newer, with source at github.com/djust-org/djust.
Community notes