neuron-explorer
Steering-first neuron explorer for Qwen3.5-4B: push every MLP neuron, keep every rollout, browse the results
Build production-ready reactive Django apps without a JavaScript application. djust pairs Django templates with a Rust VDOM engine, WebSocket recovery, and server-side state management.
Developer toolsPython · JavaScriptGitHub @djust-orgOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Phoenix LiveView-style reactive server-side rendering for Django with Rust-powered performance
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Reactive server-side rendering for Django, powered by Rust
djust brings Phoenix LiveView-style reactive components to Django. You write
server-side Python; the client updates automatically over a WebSocket. There is
no JavaScript to write, no bundler, and no build step in your project.Read the full README ↗djust brings Phoenix LiveView-style reactive views to Django. You write server-side Python; the browser updates itself over a WebSocket. There is no JavaScript to write, no bundler, and no build step in your project.
djust.org · Documentation · Quick Start · Examples
from djust import LiveView, event_handler
class CounterView(LiveView):
template = """
<div dj-root>
<h1>Count: {{ count }}</h1>
<button dj-click="increment">+</button>
<button dj-click="decrement">-</button>
</div>
"""
def mount(self, request, **kwargs):
self.count = 0
@event_handler
def increment(self):
self.count += 1 # the page updates; no JavaScript
@event_handler
def decrement(self):
self.count -= 1
The fastest start is the scaffold, which configures everything below:
pip install djust
djust new myproject
To add djust to an existing project instead:
1. Settings. Add the apps and a channel layer to settings.py:
INSTALLED_APPS = [
# ... your existing apps ...
"channels",
"djust",
]
ASGI_APPLICATION = "myproject.asgi.application"
CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}}
2. asgi.py. Route WebSockets to djust:
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
django_asgi_app = get_asgi_application() # sets Django up before the imports below
from django.contrib.staticfiles.handlers import ASGIStaticFilesHandler
from django.urls import path
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from djust.websocket import LiveViewConsumer
application = ProtocolTypeRouter({
# ASGIStaticFilesHandler serves /static/ (djust's client.js included)
# under uvicorn or daphne in development, as runserver would.
"http": ASGIStaticFilesHandler(django_asgi_app),
"websocket": AuthMiddlewareStack(
URLRouter([path("ws/live/", LiveViewConsumer.as_asgi())])
),
})
3. A view, a URL and a template.
# myapp/views.py
from djust import LiveView, event_handler
class CounterView(LiveView):
template_name = "counter.html"
def mount(self, request, **kwargs):
self.count = 0
@event_handler
def increment(self):
self.count += 1
# myproject/urls.py
from django.urls import path
from myapp.views import CounterView
urlpatterns = [path("counter/", CounterView.as_view(), name="counter")]
<!-- myapp/templates/counter.html -->
{% load live_tags %}
<!DOCTYPE html>
<html>
<head>
<title>Counter</title>
{% djust_client_config %}
</head>
<body>
<div dj-root>
<h1>Count: {{ count }}</h1>
<button dj-click="increment">+</button>
</div>
</body>
</html>
4. Run it with uvicorn myproject.asgi:application and open /counter/.
In DEBUG, djust hot-reloads views without restarting or losing state, so you
don't need --reload.
On each event djust re-renders the view on the server, diffs the result against the previous render in Rust, and sends only the patches.
| In the template | Purpose |
|---|---|
{% djust_client_config %} in <head> | Emits client config. djust injects the client runtime into every LiveView response; you never add a <script> tag. |
dj-root | Marks the reactive region. Only HTML inside it is diffed and patched. djust stamps dj-view onto it with the dotted path of the view. |
dj-view="myapp.views.MyView" | Optional. Write it yourself only to name a specific view, such as an embedded or sticky view, or a template shared by several views. |
dj-click, dj-input, dj-change, dj-submit | Send events to @event_handler methods. Inputs pass value; forms pass their fields. |
dj-key or data-key on list items | Gives items a stable identity, so reorders become moves and keep focus, scroll position and animations. |
{% for item in items %}
<div dj-key="{{ item.id }}">{{ item.name }}</div>
{% endfor %}
Without a key, lists are diffed by position: still correct, with more DOM
changes on reorders. Conditional attributes such as
class="btn {% if active %}active{% endif %}" are handled correctly too, with
or without {% else %}. See the VDOM architecture guide
and the template cheat sheet.
You don't need LiveView to use the Rust engine. Point a TEMPLATES entry at the
backend, and your existing TemplateViews, render() calls and {% include %}s
render through Rust, with no WebSocket and no client runtime:
TEMPLATES = [
{
"BACKEND": "djust.template.backend.DjustTemplateBackend",
"DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True,
"OPTIONS": {"context_processors": [...]},
},
{
# admin and contrib templates still need Django's own backend
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {"context_processors": [...]},
},
]
template_tests suite passes unmodified against this backend (1032 of the 1,047 cells that reach an engine at all; measured by scripts/run-django-template-suite.py against the Django tag matching the installed version — see docs/TEMPLATE_BACKEND.md for the full breakdown and what the remaining cells are). djust new configures this backend for you.
Full render, same template on both engines, parsed once on each side.
benchmarks/benchmark.py on an Apple silicon laptop, Django 5.2.16, Python
3.12, DEBUG=False, release build:
| Template | Rows | Django | djust | Speedup |
|---|---|---|---|---|
| Static markup | 10,000 | 2.85 ms | 2.81 ms | 1.0x |
| Simple list (2 vars/row) | 10,000 | 63.7 ms | 8.96 ms | 7.1x |
| Filtered list (typical page) | 10,000 | 241 ms | 21.5 ms | 11.2x |
The speedup grows with variable and filter density. The table leaves out the
bigger win on updates, where djust sends a diff and plain Django re-sends the
whole page. Reproduce it with make build && python benchmarks/benchmark.py.
The script refuses a debug build, which is roughly 7.6x slower.
| Topic | Guide |
|---|---|
| Directives, filters and tags | Template cheat sheet |
| Reusable components and theming | Components |
| Tailwind and Bootstrap setup | CSS frameworks |
dj-patch, dj-navigate, live_redirect() | Navigation |
@debounce, @throttle, @cache, @background | State management |
| Event handler conventions | Event handlers |
Debug panel (Ctrl/Cmd+Shift+D) | Debug panel |
| Production with uvicorn, Redis and Nginx | Deployment |
| Building with an AI coding agent | Conventions · AI references |
Everything else is at docs.djust.org. Working examples live in examples/demo_project.
Browser client runtime (~80 KB gz) ── events up, patches down
↕ WebSocket (or HTTP fallback)
Django LiveView classes, event handlers, state (Python, Channels)
↕ PyO3
Rust core template engine · VDOM diff · HTML parser · MessagePack
Building from source needs Rust 1.70+ and uv:
git clone https://github.com/djust-org/djust.git
cd djust
make install # dependencies via uv, then a release build of the Rust core
make test # Python + Rust + JavaScript
make help # everything else
See CONTRIBUTING.md and the testing guide.
CSRF protection, automatic escaping in the Rust engine, WebSocket origin
validation and session auth, rate limiting, and view- and handler-level
permissions are built in; manage.py djust_audit reports your views' auth
posture. Report vulnerabilities to [email protected] (see SECURITY.md).
MIT licensed (LICENSE). Inspired by Phoenix LiveView; built with PyO3 and html5ever.