Home Features Docs Blog Examples FAQ
Releases - Part 6 of 6

djust 0.4.0 — The Developer Experience Release

djust Team | | 6 min read
post.title

djust 0.4.0 is our biggest release since 0.3. While 0.3 brought Phoenix LiveView parity, 0.4 is about developer experience — making everyday tasks faster, safer, and more intuitive. 30+ new features, critical bug fixes, and a security hardening pass that eliminated every known vulnerability.

Highlights

1. Flash Messages

Phoenix-style ephemeral flash notifications, built in:

class OrderView(LiveView):
    @event_handler
    def place_order(self, **kwargs):
        Order.objects.create(user=self.request.user)
        self.put_flash("success", "Order placed!")
        self.put_flash("info", "Confirmation email sent.")

Messages are flushed to the client after each response over WebSocket or SSE. The {% dj_flash %} template tag renders the container with auto-dismiss, ARIA roles, and configurable positioning. Works over both WebSocket and HTTP POST fallback. Read the Flash Messages guide →

2. Declarative HTML Attributes

0.4 adds a suite of HTML attributes that eliminate boilerplate JavaScript:

  • dj-debounce="300" / dj-throttle="150" — Rate-limit any event directly in HTML
  • dj-click-away="close_dropdown" — Fire events on outside clicks
  • dj-shortcut="ctrl+k:open_search, escape:close_modal" — Declarative keyboard shortcuts with modifier support
  • dj-disable-with="Saving..." — Auto-disable submit buttons with loading text
  • dj-lock — Prevent concurrent event handler execution
  • dj-mounted="on_chart_ready" — Element lifecycle events
  • dj-cloak — Prevent flash of unconnected content
  • dj-scroll-into-view — Auto-scroll elements after DOM updates
  • dj-window-keydown.escape="close" — Window/document-scoped events

Every attribute is declarative, server-driven, and works with the existing dj-value-* param system. See the Template Cheatsheet →

3. Form Recovery on Reconnect

When a WebSocket connection drops and reconnects, form state used to be lost. Now djust automatically recovers it:

<!-- Fields with dj-change/dj-input auto-recover -->
<input dj-change="update_name" value="{{ name }}">

<!-- Opt out specific fields -->
<input dj-change="update_token" dj-no-recover>

<!-- Custom recovery for complex state -->
<div dj-auto-recover="restore_editor_state">
    <textarea id="editor">...</textarea>
</div>

Form values are compared against server-rendered defaults — only changed fields fire recovery events. Exponential backoff with jitter (AWS full-jitter strategy) prevents thundering herd on server restart. Read the Reconnection guide →

4. Scaffolding Generator

Generate a complete CRUD LiveView in one command:

python manage.py djust_gen_live blog Post title:string body:text published:boolean

# Creates:
#   blog/views.py    — LiveView with list/create/update/delete handlers
#   blog/urls.py     — live_session() routing
#   blog/templates/  — HTML template with dj-* directives
#   blog/tests.py    — Test suite

Supports all Django field types including FK relationships, --dry-run preview, and --force overwrite. Read the Scaffolding guide →

5. Dynamic Page Metadata

Update document.title and <meta> tags from any event handler — no VDOM diff needed:

class ArticleView(LiveView):
    @event_handler
    def load_article(self, slug: str = "", **kwargs):
        self.article = Article.objects.get(slug=slug)
        self.page_title = self.article.title
        self.page_meta = {
            "description": self.article.excerpt,
            "og:title": self.article.title,
            "og:image": self.article.image_url,
        }

Supports og: and twitter: meta tags with correct property attribute. Uses side-channel WebSocket messages for instant updates. Read the Document Metadata guide →

6. on_mount Hooks

Cross-cutting mount logic without modifying every view:

from djust.decorators import on_mount

@on_mount
def require_verified_email(view, request):
    if not request.user.email_verified:
        return "/verify-email"  # Redirect halts mount

@on_mount
def track_page_view(view, request):
    Analytics.track(request.user, request.path)

class DashboardView(LiveView):
    on_mount = [require_verified_email, track_page_view]

Hooks run after auth checks, before mount(). Return a URL string to redirect. Inherited via MRO with deduplication. Phoenix on_mount v0.17+ parity. Read the on_mount Hooks guide →

7. Debug Tooling

Three new tools for development:

  • manage.py djust_doctor — One-command diagnostic that checks Rust extension, Python/Django versions, Channels, Redis, templates, static files, routing, and ASGI server. Supports --json output for CI.
  • Warning interceptor — Debug panel captures [LiveView] console warnings and surfaces them as a badge.
  • Latency simulator — Test loading states with simulated network delay (50–500ms presets, custom values, jitter control).

Read the Developer Tools guide →

8. Security Hardening

We ran the full CodeQL and Dependabot security suite and fixed everything:

  • 25 CodeQL alerts resolved — UNSAFE_KEYS guards on VDOM patches reject __proto__/constructor/prototype, Object.defineProperty() for debug panel state cloning, format strings for all logging
  • 19 Dependabot alerts resolved — Django ≥4.2.29, urllib3 ≥2.6.3, setuptools ≥78.1.1, and more
  • WhiteNoise removedASGIStaticFilesHandler handles static files at the ASGI layer, making WhiteNoise redundant

Read the Security guide →

Bug Fixes

Critical reliability fixes that make 0.4 production-solid:

  • Tick/event version mismatch — Server ticks could collide with user events, silently dropping patches. Now serialized with asyncio.Lock and client-side buffering.
  • Focus lost during VDOM patches — Cursor position, selection range, and scroll position are now saved and restored around patch cycles.
  • {% if %} blocks breaking VDOM — Comment node placeholders are now included in child index resolution.
  • Template engine — Custom tag args properly serialize lists/objects, apply Django filters, and handle True/False/None literals.

Also Since 0.3.0 (0.3.1–0.3.8)

If you're upgrading from 0.3.0, here's what you missed in 0.3.1–0.3.8:

3.8x Rendering Speedup (0.3.1)

Replaced dir(self) iteration (~300 inherited Django View attributes) with targeted __dict__ + MRO walk. Combined with dj-update="ignore" optimization in the Rust VDOM diff engine, event roundtrip dropped from ~160ms to ~42ms on large pages.

SSE Fallback Transport (0.3.2)

djust now automatically falls back to Server-Sent Events when WebSocket is unavailable (corporate proxies, enterprise firewalls). EventSource for server→client push, HTTP POST for client→server events. Transport negotiation is automatic.

Background Work (0.3.2)

The @background decorator and start_async() enable long-running operations (AI generation, API calls) without blocking the UI. Loading states persist through background work via the async_pending flag. Named tasks support cancellation via cancel_async(name). Read the Loading States guide →

TypeScript & Python Type Stubs (0.3.2)

Full TypeScript definitions (djust.d.ts) for the client-side API and PEP 561 compliant type stubs (_rust.pyi) for the Rust extension. IDE autocomplete and mypy type checking now work out of the box.

6 New Rust Template Tags (0.3.4)

{% widthratio %}, {% firstof %}, {% templatetag %}, {% spaceless %}, {% cycle %}, and {% now %} — all 57 Django built-in template filters are now supported. CSS Frameworks guide →

djust-deploy CLI (0.3.5)

Deploy to djustlive.com from the command line: djust-deploy login, djust-deploy deploy <project>, djust-deploy status. Read the djust-deploy guide →

FormMixin Production-Ready (0.3.7)

Six fixes that unblocked FormMixin with ModelForm over WebSocket: proper @event_handler decorators on submit_form()/validate_field(), FK serialization, form data re-hydration after WS session restore, and auto-populated form_choices. Read the Forms guide →

Codebase Cleanup (0.3.7)

Seven DRY refactors extracted shared patterns into reusable helpers: reinitAfterDOMUpdate(), addEventContext(), isWSConnected(), clearOptimisticPending(), get_djust_config(), BackendRegistry, and is_model_list().

Reliability Fixes (0.3.6–0.3.8)

  • Multi-tab VDOM cache collision — Each tab now gets its own VDOM baseline keyed by URL
  • Canvas elements cleared during morphwidth/height preserved for Chart.js compatibility
  • dj-hook elements not re-initializedupdateHooks() now called in all DOM replacement paths (Hooks guide)
  • Checkbox/radio/select state sync — VDOM patches now sync DOM properties alongside HTML attributes
  • {% load %} tags preserved — Custom tag libraries survive Rust template inheritance
  • Quoted tag arguments with spacesname="My App" no longer split into separate tokens
  • Prefetch set cleared on SPA navigation — Links are properly prefetched after live_redirect
  • dj-patch on selects/inputs — Now uses WebSocket url_change instead of full page reload

Breaking: model.id returns native type (0.3.6)

model.id previously returned a string; it now returns the native Python type (int, UUID, etc.). If your templates compare model.id against string literals, update them to use the native type.

Breaking Changes

  • requires-python bumped from ≥3.8 to ≥3.9 (Python 3.8 is EOL)
  • Django minimum bumped to ≥4.2.29 for security fixes
  • whitenoise removed from dependencies — use ASGIStaticFilesHandler instead

Upgrading

pip install djust==0.4.0

New to djust? Start with the Getting Started guide. For the full changelog, see the CHANGELOG.

Share this post

Related Posts