On this page
reactivity
introduction
dframework provides two complementary reactivity systems that keep the user interface in sync with server state over the socket layer. no client side rendering framework is involved. the server renders html and the framework delivers it surgically to the correct dom target.
choosing a system
| feature | d-wire | d-live |
|---|---|---|
| trigger | an explicit user action | a database write |
| best for | counters, search, modals, forms | feeds, dashboards, live lists |
| requires | a named socket handler | a d-live attribute, no handler |
| rendering | the handler calls render | the framework refetches automatically |
use d-wire when the user expects a specific result from a deliberate action. use d-live when a region must stay in sync with data regardless of what caused it to change.
d-wire event driven reactivity
attach d-wire to any html element. when triggered, the element emits a named event over the socket to a socket handler. the handler renders a partial and the framework replaces the target container with the result.
1<button d-wire="counter:increment" d-target="#counter">+1</button>
passing data
use d-wire-data to send a payload with the event. it accepts key value pairs separated by commas or raw json.
1<button[object Object]>2 d-wire="post:like"[object Object]>3 d-wire-data="id:{{ post.id }}, type:'post'"[object Object]>4 d-target="#likes-{{ post.id }}">5 like6</button>7 8<button[object Object]>9 d-wire="user:update"[object Object]>10 d-wire-data='{"id": {{ user.id }}, "status": "active"}'[object Object]>11 d-target="#user-status">12 activate13</button>the payload is merged with any form data present and accessible as the request body in the handler.
backend handler
a d-wire handler must call render or json. the rendered html replaces the target element on the client. the framework hashes the rendered output and suppresses the update if the content has not changed, preventing redundant dom operations.
to prevent arbitrary dom injection, you must restrict which html elements a route can update by chaining targets() to your route definition.
1Socket.on('counter:increment', async (req, res) => {2 const count = await Counter.increment(req.body.id);3 return res.render('partials.counter', { count });4}).targets(['#counter']);5 6Socket.on('user:search', async (req, res) => {7 const users = await User.where('name', 'LIKE', `%${req.body.q}%`).limit(10).get();8 return res.render('partials.user-list', { users });9}).targets(['#user-list']);
trigger types
d-wire binds to the natural event for each element type.
| element | default trigger |
|---|---|
<button>, <a> |
click |
<form> |
submit |
<input>, <select> |
change |
override with d-trigger.
1<!-- fire on keyup with debounce handled server side -->2<input d-wire="user:search" d-target="#results" d-trigger="keyup" placeholder="search users">special trigger values are available.
| value | behaviour |
|---|---|
load |
fires once immediately when the element mounts |
poll-{ms} |
fires continuously every n milliseconds, e.g. poll-5000 |
programmatic dispatch
emit a d-wire event from javascript or from inside a frontend component.
1Socket.emit('dashboard:refresh', {}, '#stats-panel');2 3// from a component4this.wire('notification:dismiss', { id: this.notificationId });
d-live state driven reactivity
d-live subscribes a dom element to a database table. whenever any model write touches that table, the framework evaluates active subscriptions, notifies matching clients, and the element refetches and replaces its own content. no backend handler is needed.
1<div d-live="posts">2 @foreach(const post of await Post.where({ status: 'published' }).orderBy('created_at', 'DESC').get())3 <article>4 <h2>{{ post.title }}</h2>5 <p>{{ post.excerpt }}</p>6 </article>7 @endforeach8</div>multiple elements can subscribe to the same or different tables simultaneously on a single page.
filtering
d-live-filter restricts updates to rows matching specific criteria. the server evaluates filters against the changed row before notifying clients. clients that do not match the filter receive no message and perform no refetch.
1<div d-live="orders" d-live-filter="user_id:{{ user.id }}">...</div>supported operators include.
| operator | example | meaning |
|---|---|---|
= (default) |
status:published |
equals |
!= |
status:!=archived |
not equal |
> |
price:>100 |
greater than |
< |
count:<5 |
less than |
>= |
rating:>=4 |
greater than or equal |
<= |
stock:<=10 |
less than or equal |
multiple filters use and logic. use commas to separate them.
1<div d-live="messages" d-live-filter="room_id:{{ room.id }},status:visible">...</div>
how it works
- on mount, the element sends a subscribe message over the socket, registering itself for the table with any filter criteria.
- when the query builder executes a write, it notifies the socket router with the table name and the changed data.
- the router iterates active subscriptions, evaluates each filter against the changed data, and emits a notification only to clients whose filter matched.
- the subscribed element rerenders its own content. updates are debounced to one hundred milliseconds to handle bulk operations efficiently. updates are suppressed for two hundred and fifty milliseconds after a successful form submission to prevent double fetches.
surgical state sync
push state directly into a component's reactive memory without rerendering its template. this is useful for lightweight state updates that do not require a full html replacement.
1// backend, emit to a specific component by selector2Socket.emit('d-sync:state', { unread: 14 }, '#notification-badge');the targeted component calls its set state method automatically, triggering its render and effect loops.
error handling
spa and reactivity integrate with the abort helper.
form submissions use standard fetch requests. calling the abort method returns a structured json error object that the form renders inline next to the relevant inputs.
spa navigations use background requests. calling the abort method renders the full error page html, which the spa layer displays and reflects in the url. socket routing handlers also gracefully catch and log any rendering exceptions, falling back to a client safe error message when necessary.

