Home Features Docs Blog Philosophy Examples FAQ Live Demo Hosting

Quick Start Guide

From git clone to a running real-time app in under 2 minutes. No JavaScript required.

🎮
Want to see it first? Try the live demo →
start.djust.org is a real, multi-user djust app running in your browser — click reactions, vote in a poll, post to a guestbook, and watch it sync for everyone in real time. No install. The Python behind each interaction is shown right on the page.
Fastest start

Clone the starter

djust-start is a minimal, opinionated djust project — everything wired up so you go from git clone to a running app with a live counter on :8000 in under 2 minutes.

# Clone the starter and step in
git clone https://github.com/djust-org/djust-start.git myapp
cd myapp

# Install dependencies and start the dev server
make install
make dev

The server comes up on http://localhost:8000 with a working live counter — click the buttons and watch it update in real time, no full page reload.

View the djust-start repo Prereqs: Python 3.12+, uv, make.
The alternative

Prefer to set it up manually?

Wire djust into a Django project from scratch — the same end result as the starter, step by step. Reach for this when you're adding djust to an existing app.

Prerequisites

Python 3.10+
Check: python --version
Django 4.2+
Or we'll install it for you
Basic Django knowledge
Views, templates, URLs
1

Install djust

Install djust via pip. This will also install Django and other dependencies.

# Create and activate a virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install djust
pip install djust
2

Create a Django Project

Create a new Django project or use an existing one.

# Create new Django project
django-admin startproject myproject
cd myproject

# Create an app
python manage.py startapp counter
3

Configure Django Settings

Add djust and Channels to your INSTALLED_APPS in settings.py

# myproject/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'channels',  # Required for WebSocket
    'djust',     # djust framework
    'counter',   # Your app
]

# Required: point Django at your ASGI application
ASGI_APPLICATION = 'myproject.asgi.application'

# Channel layer (in-memory for development)
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels.layers.InMemoryChannelLayer'
    }
}
4

Setup ASGI Routing

Configure WebSocket routing in asgi.py

# myproject/routing.py
from django.urls import path
from djust.websocket import LiveViewConsumer

websocket_urlpatterns = [
    path('ws/live/', LiveViewConsumer.as_asgi()),
]

# myproject/asgi.py
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import myproject.routing

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": AuthMiddlewareStack(
        URLRouter(myproject.routing.websocket_urlpatterns)
    ),
})
5

Create Your First LiveView

Create a simple counter with real-time updates in counter/views.py

# counter/views.py

from djust import LiveView
from djust.decorators import event_handler

class CounterView(LiveView):
    template_name = 'counter/counter.html'

    def mount(self, request, **kwargs):
        self.count = 0

    def get_context_data(self, **kwargs):
        return {'count': self.count}

    @event_handler()
    def increment(self, **kwargs):
        self.count += 1

    @event_handler()
    def decrement(self, **kwargs):
        self.count -= 1

    @event_handler()
    def reset(self, **kwargs):
        self.count = 0
6

Create the Template

Create counter/templates/counter/counter.html

<!-- counter/templates/counter/counter.html -->
{% load djust_tags %}
<!DOCTYPE html>
<html>
<head>
    <title>Counter Example</title>
    {% djust_scripts %}
</head>
<body dj-view="{{ dj_view_id }}">
    <div dj-root style="text-align: center; padding: 2rem;">
        <h1>Counter: {{ count }}</h1>
        <button dj-click="decrement">-</button>
        <button dj-click="reset">Reset</button>
        <button dj-click="increment">+</button>
    </div>
</body>
</html>
Note:dj-view on <body> establishes the WebSocket connection. dj-root marks the reactive region that gets patched on updates. Both are required.
7

Configure URLs

Add the URL pattern to myproject/urls.py

# myproject/urls.py

from django.contrib import admin
from django.urls import path
from counter.views import CounterView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', CounterView.as_view(), name='counter'),
]
8

Run the Development Server

Start the server and see your counter in action!

uvicorn myproject.asgi:application --reload
Important: Use uvicorn (or daphne), not manage.py runserver — the standard Django dev server is WSGI-only and does not support WebSockets.
Success!
Open http://localhost:8000 in your browser. Click the buttons and watch the counter update in real-time!

How It Works

1
Initial Load
When you visit the page, Django renders the template with count = 0 and sends full HTML to the browser.
2
WebSocket Connection
The djust client JavaScript establishes a WebSocket connection to the server, creating a live session.
3
User Clicks Button
When you click "increment", the client sends an event to the server: {"event": "increment"}
4
Server Updates State
The server calls self.increment(), updating self.count = 1
5
VDOM Diff (Rust)
djust's Rust engine re-renders the template and compares it to the previous version, generating minimal patches in <100μs.
6
Client Updates DOM
The client receives patches like [{path: [2,0], text: "1"}] and updates only the counter number. No full page reload!

Directive Quick Reference

Browse all directives

Now that your first view works, here's the dj-* vocabulary you'll reach for next.

Event attributes
dj-click      dj-submit
dj-change     dj-input
dj-blur       dj-focus
dj-keydown    dj-keyup
dj-poll       dj-copy
dj-confirm    dj-model
dj-mounted    dj-shortcut
dj-click-away
Navigation
dj-patch      # swap dj-root
dj-navigate   # SPA nav + history
dj-prefetch   # warm cache on hover
Window & document scoping
dj-window-keydown
dj-window-scroll   # 150ms throttle
dj-window-resize   # 150ms throttle
dj-window-click
dj-document-keydown
dj-document-click
Rate limiting
dj-debounce="300"  # ms
dj-debounce="blur" # until blur
dj-throttle="500"  # ms
Loading states
dj-loading          # toggle class
dj-loading.hide     # hide
dj-loading.show     # spinner
dj-loading.disable  # disable
dj-disable-with="…"
dj-lock
UI feedback & connection
dj-cloak            # hide til live
dj-scroll-into-view
.dj-connected       # body class
.dj-disconnected    # body class

Need Help?

Stuck on something? Check out the full documentation or browse working examples.