---
# GENERATED FILE — do not edit.
# Source: `manage.py docs_generate` (djust 1.2.0).
# Edit the generator in docs_app/generator.py instead.
title: "Directive reference"
description: "All 68 dj-* directives, generated from the framework."
level: reference
order: 10
section: reference
generated: true
---
Every `dj-*` directive djust understands, generated from
`djust.schema.get_surface_manifest()` in the pinned framework.

**68 directives** across 13 categories.
This page is generated — if a directive is missing here it is missing
from the framework, not from the docs.

## Categories

- [Events](#events) — 18
- [Two-way binding](#two-way-binding) — 2
- [Loading states](#loading-states) — 9
- [DOM manipulation](#dom-manipulation) — 7
- [Animation & transitions](#animation--transitions) — 5
- [Navigation](#navigation) — 3
- [Streaming](#streaming) — 4
- [Uploads](#uploads) — 4
- [Performance](#performance) — 5
- [Recovery](#recovery) — 1
- [Modifiers](#modifiers) — 8
- [Hooks](#hooks) — 1
- [Client](#client) — 1

## Events

### `dj-blur`

Send event when element loses focus

**Value:** `handler_name`

```html
<input dj-blur="validate_field" name="email">
```

### `dj-change`

Send event on input change (fires on blur for text, immediately for checkboxes/selects)

**Value:** `handler_name`

```html
<select dj-change="filter_by"><option>All</option></select>
```

### `dj-click`

Send event to server on click

**Value:** `handler_name or handler_name('arg1', arg2)`

```html
<button dj-click="increment">+1</button>
```

### `dj-click-away`

Fire a server event when a click occurs outside this element (commonly used to close dropdowns/popovers).

**Value:** `handler_name`

```html
<div dj-click-away="close_menu">...</div>
```

### `dj-document-*`

Bind a keydown/keyup/click/scroll/resize listener scoped to `document` instead of the element. Supports an optional '.key' modifier suffix for key events.

**Value:** `handler_name`

```html
<div dj-document-click="track_page_click">...</div>
```

### `dj-focus`

Send event when element receives focus

**Value:** `handler_name`

```html
<input dj-focus="track_focus" name="search">
```

### `dj-input`

Send event on every keystroke (auto-debounced 300ms for text, throttled for range/number)

**Value:** `handler_name`

```html
<input dj-input="search" name="query">
```

### `dj-keydown`

Send event on keydown. Supports key modifiers (e.g., dj-keydown.enter).

**Value:** `handler_name or handler_name.enter`

```html
<input dj-keydown.enter="submit_search">
```

### `dj-keyup`

Send event on keyup. Supports key modifiers like dj-keydown.

**Value:** `handler_name or handler_name.escape`

```html
<input dj-keyup.escape="clear_search">
```

### `dj-mounted`

Fire a server event the first time this element enters the DOM after initial page mount (e.g. an element revealed by a later {% if %} patch or a modal that opens later). Fires once per DOM node.

**Value:** `handler_name`

```html
<div dj-mounted="chart_ready" data-value-chart-id="{{ id }}">...</div>
```

### `dj-mouseenter`

Send event when the pointer enters the element. Non-bubbling: entering a child fires neither the parent's dj-mouseleave nor a second dj-mouseenter.

**Value:** `handler_name`

```html
<div dj-mouseenter="preview">Hover me</div>
```

### `dj-mouseleave`

Send event when the pointer leaves the element. Non-bubbling: moving into a child element does not fire it.

**Value:** `handler_name`

```html
<div dj-mouseenter="preview" dj-mouseleave="hide_preview">Hover me</div>
```

### `dj-mutation`

Fire a server event when this element's attributes or children change (bridges third-party widgets — charts, maps, rich-text editors — that mutate the DOM outside djust's control). Observes attribute changes if dj-mutation-attr is set, otherwise observes childList changes.

**Value:** `handler_name`

```html
<div dj-mutation="on_change" dj-mutation-attr="class,style">...</div>
```

**Related**

- `dj-mutation-attr` — Comma-separated attribute names to observe (default: observe childList instead).
- `dj-mutation-debounce` — Coalesce bursts of mutations within N ms into one server event (default: 150).

### `dj-paste`

Fire a server event on paste, with structured payload (text, html, and file metadata from the clipboard). Pasted files flow through the dj-upload pipeline rather than the WebSocket event frame.

**Value:** `handler_name`

```html
<div dj-paste="handle_paste" contenteditable>...</div>
```

**Related**

- `dj-paste-suppress` — Prevent the browser's default paste behavior after dispatch.

### `dj-poll`

Declarative polling: periodically send event to server. Pauses when tab is hidden.

**Value:** `handler_name`

```html
<div dj-poll="refresh_data" dj-poll-interval="5000">...</div>
```

### `dj-shortcut`

Bind one or more keyboard shortcuts to server events. Comma-separated bindings of the form '[modifier+...]key:handler[:prevent]'. Ignored while a form input has focus unless dj-shortcut-in-input is also present.

**Value:** `'ctrl+k:open_search,escape:close_modal:prevent'`

```html
<body dj-shortcut="ctrl+k:open_search,escape:close_modal:prevent">
```

**Related**

- `dj-shortcut-in-input` — Opt-in flag: also fire the shortcut while a form field has focus.

### `dj-submit`

Send form data to server on submit. Calls e.preventDefault() and e.target.reset().

**Value:** `handler_name`

```html
<form dj-submit="create_item"><input name="title"><button>Add</button></form>
```

### `dj-window-*`

Bind a keydown/keyup/click/scroll/resize listener scoped to `window` instead of the element — for global shortcuts and viewport-level events. Supports an optional '.key' modifier suffix for key events.

**Value:** `handler_name`

```html
<div dj-window-keydown.escape="close_modal">...</div>
```

## Two-way binding

### `dj-force-value`

Opt a focused form field into server-authoritative value sync during VDOM patches (by default, focused fields are skipped to preserve what the user is typing).

**Value:** `(no value needed)`

```html
<input dj-model="draft" dj-force-value>
```

### `dj-model`

Two-way data binding between form element and server state. Syncs on 'input' event by default.

**Value:** `field_name`

```html
<input type="text" dj-model="search_query">
```

## Loading states

### `dj-disable-with`

Disable the element and swap its text while its triggering event is in flight; original text is restored on response.

**Value:** `loading text`

```html
<button dj-click="save" dj-disable-with="Saving...">Save</button>
```

### `dj-form-pending`

React to an ancestor form's dj-submit being in-flight, without writing a hook. React 19 useFormStatus equivalent.

**Value:** `'hide' | 'show' | 'disabled'`

```html
<form dj-submit="save"><span dj-form-pending="show">Saving...</span></form>
```

### `dj-loading`

Shorthand form: disable this element while the named event is in flight (equivalent to dj-loading.disable, but scoped to a specific event name rather than the element's own triggering event).

**Value:** `event_name`

```html
<button dj-loading="save">Save</button>
```

### `dj-loading.class`

Add CSS class while event is in flight

**Value:** `class_name`

```html
<button dj-click="save" dj-loading.class="opacity-50">Save</button>
```

### `dj-loading.disable`

Disable element while event is in flight

**Value:** `(no value needed)`

```html
<button dj-click="save" dj-loading.disable>Save</button>
```

### `dj-loading.for`

Explicit form of the dj-loading shorthand: works on any element (not just the one triggering the event) — react while the named event is in flight anywhere on the page.

**Value:** `event_name`

```html
<span dj-loading.for="save">Saving...</span>
```

### `dj-loading.hide`

Hide element while event is in flight

**Value:** `(no value needed)`

```html
<div dj-loading.hide>Normal content</div>
```

### `dj-loading.show`

Show element (set display) while event is in flight

**Value:** `display_value (default: 'block')`

```html
<div dj-loading.show="flex" style="display:none">Loading...</div>
```

### `dj-lock`

Prevent duplicate submissions: locks the element (disables form fields, adds a CSS class otherwise) on first trigger and skips subsequent triggers until the response unlocks it.

**Value:** `(no value needed)`

```html
<button dj-click="submit_order" dj-lock>Place Order</button>
```

## DOM manipulation

### `dj-cloak`

Hide this element (display:none via injected CSS) until the LiveView mount completes — prevents a flash of unstyled/un-hydrated content. Removed automatically once mounted.

**Value:** `(no value needed)`

```html
<div dj-cloak>Loading skeleton...</div>
```

### `dj-dialog`

Native <dialog> modal integration. Toggling the value between 'close'/'open' calls showModal()/close() on the element.

**Value:** `'open' | 'close'`

```html
<dialog id="settings" dj-dialog="{{ show_settings|yesno:'open,close' }}">...</dialog>
```

**Related**

- `dj-dialog-close-event` — Server event fired when the user closes the dialog client-side (Escape, backdrop click), keeping server state in sync.

### `dj-ignore-attrs`

Mark HTML attributes as client-owned so VDOM SetAttr patches skip them — for browser-native elements (<dialog open>, <details open>) and third-party JS that manages attributes the server doesn't know about.

**Value:** `comma-separated attribute names`

```html
<dialog dj-ignore-attrs="open">...</dialog>
```

### `dj-scroll-into-view`

Automatically scroll this element into view after a DOM update.

**Value:** `(no value needed) | scrollIntoView options string`

```html
<div id="latest-message" dj-scroll-into-view></div>
```

### `dj-sticky-scroll`

Keep a scrollable container pinned to the bottom as new content is appended, backing off (and re-arming) based on user scroll position.

**Value:** `(no value needed)`

```html
<div dj-sticky-scroll style="overflow-y: auto; height: 400px">...</div>
```

### `dj-target`

Scope the server re-render to a specific element (CSS selector)

**Value:** `CSS selector (e.g., #sidebar, .panel)`

```html
<button dj-click="refresh_sidebar" dj-target="#sidebar">Refresh</button>
```

### `dj-update`

Control how server HTML updates are applied to this element

**Value:** `append | prepend | replace | ignore`

```html
<ul dj-update="append" id="messages">{% for msg in messages %}<li>{{ msg }}</li>{% endfor %}</ul>
```

## Animation & transitions

### `dj-flip`

FLIP-technique reorder animation: when a child of this element reorders, animate it from its old screen position to its new one.

**Value:** `(no value needed)`

```html
<ul dj-flip>{% for item in items %}<li id="item-{{ item.id }}">{{ item.label }}</li>{% endfor %}</ul>
```

**Related**

- `dj-flip-duration` — Transition duration in ms (default: 300).
- `dj-flip-easing` — CSS easing function for the transform transition.

### `dj-remove`

Declarative CSS exit transition. When a VDOM patch would remove this element, djust delays the removal until the transition completes (or a fallback timeout fires).

**Value:** `'start active end' | 'class-name'`

```html
<li dj-remove="fade-out">Toast</li>
```

**Related**

- `dj-remove-duration` — Override the fallback timeout in ms (default: 600, clamped 0-30000).

### `dj-transition`

Declarative CSS enter transition. Three-token form 'start active end' applies each class in a phase; single-token short form applies one class and waits for transitionend.

**Value:** `'start active end' | 'class-name'`

```html
<div dj-transition="opacity-0 transition-opacity-300 opacity-100">Fades in</div>
```

### `dj-transition-group`

Orchestrate enter/leave animations for a list of children by wiring dj-transition/dj-remove specs onto each child automatically.

**Value:** `(no value, use dj-group-enter/dj-group-leave) | 'enter-spec | leave-spec'`

```html
<ul dj-transition-group dj-group-enter="opacity-0 transition-opacity-300 opacity-100" dj-group-leave="opacity-100 transition-opacity-300 opacity-0">...</ul>
```

**Related**

- `dj-group-enter` — Enter transition spec applied to newly-added children.
- `dj-group-leave` — Leave transition spec applied to removed children.
- `dj-group-appear` — Also animate children present on first render (not just later additions).

### `dj-view-transitions`

Opt in to wrapping VDOM patches in the browser's document.startViewTransition() API (cross-fade by default; view-transition-name enables shared-element morphs). Placed on <body>. No-ops gracefully when unsupported (Firefox) or when the user has requested prefers-reduced-motion: reduce.

**Value:** `(no value needed)`

```html
<body dj-view-transitions>
```

## Navigation

### `dj-navigate`

Navigate to a different LiveView over the existing WebSocket (no page reload)

**Value:** `URL path`

```html
<a dj-navigate="/items/42/">View Item</a>
```

### `dj-patch`

Update URL params without remounting the view (client-side pushState + server url_change)

**Value:** `URL path or query string`

```html
<a dj-patch="?page=2&sort=name">Page 2</a>
```

### `dj-patch-reload`

Opt-in escape hatch: force a full page navigation for this dj-patch/dj-navigate link instead of an in-place client-side URL update.

**Value:** `(no value needed)`

```html
<a dj-patch="?page=2" dj-patch-reload>Page 2</a>
```

## Streaming

### `dj-stream`

Mark element as a stream target for server-pushed DOM operations

**Value:** `stream_name`

```html
<ul dj-stream="messages">{% for msg in streams.messages %}<li>{{ msg }}</li>{% endfor %}</ul>
```

### `dj-stream-mode`

Default insertion mode for streaming text content

**Value:** `append | replace | prepend`

```html
<div dj-stream="output" dj-stream-mode="append"></div>
```

### `dj-viewport-bottom`

Fire a server event once when the last child of this container enters the viewport (bidirectional infinite scroll, forward pagination).

**Value:** `handler_name`

```html
<div dj-stream="messages" dj-viewport-bottom="load_newer">...</div>
```

**Related**

- `dj-viewport-threshold` — IntersectionObserver threshold, 0-1 (default: 0.1).

### `dj-viewport-top`

Fire a server event once when the first child of this container enters the viewport (bidirectional infinite scroll, backward pagination).

**Value:** `handler_name`

```html
<div dj-stream="messages" dj-viewport-top="load_older">...</div>
```

**Related**

- `dj-viewport-threshold` — IntersectionObserver threshold, 0-1 (default: 0.1).

## Uploads

### `dj-upload`

Bind a file input to an upload slot (binary WebSocket upload)

**Value:** `upload_slot_name`

```html
<input type="file" dj-upload="avatar">
```

### `dj-upload-drop`

Mark element as a drag-and-drop zone for file uploads

**Value:** `upload_slot_name`

```html
<div dj-upload-drop="attachments">Drop files here</div>
```

### `dj-upload-preview`

Container for image upload previews (auto-populated with thumbnails)

**Value:** `upload_slot_name`

```html
<div dj-upload-preview="avatar"></div>
```

### `dj-upload-progress`

INERT — registered as an attribute but no client code reads it; no progress bar is created. Render progress server-side instead.

**Value:** `upload_slot_name`

```html
<div dj-upload-progress="attachments"></div>
```

## Performance

### `dj-key`

Stable identity for an item in a list, so the VDOM differ can MOVE a row instead of destroying and rebuilding it when the list reorders. Without it the differ falls back to positional diffing, which discards the DOM node — losing focus, input values, scroll position and any client-side state attached to it. Read by the Rust template parser into VNode.key (crates/djust_vdom/src/parser.rs), where `data-key` is accepted as an equivalent spelling. Keys must be UNIQUE among siblings: a duplicate is ambiguous, so the differ demotes those siblings to positional diffing and warns DJE-051. Prefer this over an authored dj-id, which the parser ignores (#1253).

**Value:** `unique_value_per_item`

```html
{% for item in items %}<li dj-key="{{ item.id }}">{{ item.name }}</li>{% endfor %}
```

**Related**

- `data-key` — Equivalent spelling, accepted by the same parser branch. Also the default height-cache key for dj-virtual in variable-height mode (see dj-virtual-key-attr).

### `dj-lazy`

Defer WebSocket connection and LiveView mounting until the element enters the viewport, is clicked, or is hovered — reduces memory/connections for below-fold LiveViews.

**Value:** `'viewport' (default) | 'click' | 'hover'`

```html
<div dj-view="my_view" dj-lazy>...</div>
```

### `dj-prefetch`

Prefetch a same-origin link's page via the service worker when the user hovers over it. Value 'false' opts a link out.

**Value:** `(no value needed) | 'false'`

```html
<a href="/reports/" dj-prefetch>View reports</a>
```

### `dj-track-static`

Detect stale static assets (script/link) after a WebSocket reconnect and dispatch a dj:stale-assets event, or reload the page automatically when the value is 'reload'.

**Value:** `(no value needed) | 'reload'`

```html
<script dj-track-static src="{% static 'js/app.abc123.js' %}"></script>
```

### `dj-virtual`

Render only the visible slice of a large list (windowed/virtual list with DOM recycling). Value names the context list variable driving the list.

**Value:** `items_var_name`

```html
<div dj-virtual="items" dj-virtual-item-height="48">...</div>
```

**Related**

- `dj-virtual-item-height` — Fixed pixel height per item.
- `dj-virtual-variable-height` — Opt into variable heights, measured via ResizeObserver.
- `dj-virtual-overscan` — Rows rendered above/below the viewport (default: 3).
- `dj-virtual-estimated-height` — Baseline height for unmeasured items in variable mode (default: 50).
- `dj-virtual-key-attr` — Attribute used as the height-cache key in variable mode (default: 'data-key').

## Recovery

### `dj-auto-recover`

After a WebSocket reconnect, serialize this container's form field values and data-* attributes and fire the named event so the server can restore client-only state that didn't round-trip through the view.

**Value:** `handler_name`

```html
<div dj-auto-recover="restore_draft">...</div>
```

**Related**

- `dj-no-recover` — Exclude an individual field within (or outside) an auto-recover container from recovery serialization.

## Modifiers

### `dj-confirm`

Show browser confirm() dialog before sending event. Cancels if user declines.

**Value:** `confirmation message`

```html
<button dj-click="delete_item" dj-confirm="Are you sure?">Delete</button>
```

### `dj-debounce`

Debounce a rate-limited event directive (dj-input/dj-change/dj-keydown/dj-keyup) by N milliseconds, or 'blur' to defer until the field loses focus. Takes precedence over the legacy data-debounce attribute.

**Value:** `milliseconds | 'blur'`

```html
<input dj-input="search" dj-debounce="500">
```

### `dj-no-submit`

Swallow Enter-key submits from text inputs in this form (the 'enter' mode); prevents accidental submission on multi-field forms.

**Value:** `'enter'`

```html
<form dj-submit="save" dj-no-submit="enter">...</form>
```

### `dj-params`

DEPRECATED (0.3.2 -> 0.3.6+ migration): JSON blob of extra params to send with an event. Use dj-value-* attributes instead — data-*/dj-value-* keys take precedence over dj-params keys with the same name.

**Value:** `'{"key": "value"}' JSON blob`

```html
<button dj-click="save" dj-params='{"source": "toolbar"}'>Save</button>
```

### `dj-poll-interval`

Polling interval in milliseconds for dj-poll (default: 5000)

**Value:** `milliseconds`

```html
<div dj-poll="refresh" dj-poll-interval="3000">...</div>
```

### `dj-throttle`

Throttle a rate-limited event directive to at most once per N milliseconds. Takes precedence over the legacy data-throttle attribute.

**Value:** `milliseconds`

```html
<input dj-input="live_search" dj-throttle="200">
```

### `dj-trigger-action`

Native POST bridge: when the server pushes a trigger-action event, submit this form natively (full page navigation) instead of over the WebSocket — for flows that need a real HTTP redirect (e.g. payment providers).

**Value:** `(no value needed)`

```html
<form action="/checkout/" method="post" dj-trigger-action>...</form>
```

### `dj-value-*`

Pass static context alongside any event directive as typed params (Phoenix LiveView's phx-value-* equivalent). Supports the same ':type' coercion suffixes as data-* attributes.

**Value:** `any string (kebab-case suffix -> snake_case param key)`

```html
<button dj-click="delete" dj-value-item-id:int="{{ item.id }}">Delete</button>
```

## Hooks

### `dj-hook`

Attach a client-side JS hook to this element. Hook receives mounted/updated/destroyed callbacks.

**Value:** `HookName (registered in window.djust.hooks)`

```html
<canvas dj-hook="MyChart" data-values="{{ chart_data }}"></canvas>
```

**Related**

- `dj-hook-value-*` — Typed value exposed to the hook as this.values.<camelCase> (ADR-025). e.g. dj-hook-value-code='"007"' -> this.values.code. Kebab-case attribute suffix maps to camelCase property; read-only from JS.
- `dj-hook-target` — Marks a descendant of the hook's element as a named scoped query target, retrieved via this.target(name) / this.targets(name).

## Client

### `dj-copy`

Copy attribute value to clipboard on click (client-only, no server event)

**Value:** `text to copy (can use template variables)`

```html
<button dj-copy="{{ api_key }}">Copy Key</button>
```

**Related**

- `dj-copy-class` — CSS class applied to the button while showing feedback (default: 'dj-copied')
- `dj-copy-event` — Server event name to also fire after a successful copy
- `dj-copy-feedback` — Temporary button text shown after copying (default: 'Copied!')
