Live Updates
Non-disruptive real-time data updates with buffering and user-controlled application.
Overview
Writing rows straight into a filtered, sorted, paged table is disorienting: rows jump
position mid-read, disappear behind an active filter, or land in a selection the reader is
still building. enableLiveUpdates buffers them instead.
A banner above the rows counts what is waiting (“3 new, 2 updated, 1 deleted”) and
the reader merges it with a click. By default the table also merges at the next sort, filter or
page change, when the view is reorganizing anyway.
You push changes from any data source (WebSocket, SSE, polling) through three methods on the
table context: pushInsert, pushUpdate and pushDelete. Rows are matched by item.id, and pushUpdate(id, changes) merges changes into the row, so a message carrying only { status } leaves the other fields alone.
Demo
The panel below plays the role of your backend. Push a few events and the LiveUpdateBanner appears above the rows, counting
what is pending. Apply merges the buffer into the table, Dismiss drops it. The demo passes autoApplyOnNavigation={false} so pending changes always
wait for your click.
Simulated server feed
Stand-in for a WebSocket/SSE handler: each button calls the corresponding push method.
Order | Customer | Status | Total | Placed |
|---|---|---|---|---|
ORD-1008 | Helix Health | pending | 310 | 2026-07-13 |
ORD-1006 | Fjord Analytics | shipped | 1780 | 2026-07-12 |
ORD-1007 | Granite & Co | pending | 420 | 2026-07-12 |
ORD-1004 | Delta Foods | pending | 95 | 2026-07-11 |
ORD-1005 | Ember Studio | paid | 640 | 2026-07-11 |
ORD-1003 | Cobalt Works | paid | 2150 | 2026-07-10 |
ORD-1002 | Baltic Trade | shipped | 380 | 2026-07-09 |
ORD-1001 | Aurora Labs | delivered | 1240 | 2026-07-08 |
Enabling live updates
Enable live updates
<Table
{items}
{columns}
enableLiveUpdates
/>Push changes from your data source
<script>
import { Table, type TableContext } from '@urbicon-ui/table';
let table = $state<TableContext | null>(null);
$effect(() => {
const ctx = table;
if (!ctx) return;
const socket = new WebSocket('wss://api.example.com/orders');
socket.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'order:created') ctx.pushInsert(message.order);
if (message.type === 'order:updated') ctx.pushUpdate(message.id, message.changes);
if (message.type === 'order:deleted') ctx.pushDelete(message.id);
};
return () => socket.close();
});
</script>
<Table
{items}
{columns}
enableLiveUpdates
onReady={(context) => (table = context)}
/>Alternative: a feed component inside the table tree
<script>
// LiveFeed.svelte calls getTableContext(), so it must render inside the table
import { Table, SmartFilterBar } from '@urbicon-ui/table';
import LiveFeed from './LiveFeed.svelte';
</script>
<Table {items} {columns} enableLiveUpdates>
{#snippet toolbar()}
<LiveFeed />
<SmartFilterBar />
{/snippet}
</Table>Buffer & merge semantics
The buffer holds at most one pending outcome per row, so a busy feed cannot make it grow
past the number of rows it touched. A second pushInsert replaces the first, consecutive pushUpdate calls merge into one, a pushDelete for a row that is still a pending insert
cancels both, and a pushUpdate for one is folded into the
insert so the row lands already updated. Order of arrival does not matter.
Applying runs deletes, then updates, then inserts. A row that just took an update is
highlighted for three seconds, so the change is findable in a long list, and a deleted row
drops out of the selection along with the table. Updates and deletes are matched against all
of items, not just the rows on screen, so a filter or
a page never swallows one; an id that is nowhere in the data is skipped, with a warning in
dev.
Auto-apply on navigation
With autoApplyOnNavigation (default true), pending changes are merged automatically when
the user changes page, sort, filter or search: the view is reorganizing anyway, so merging
at that moment is non-disruptive. Set it to false to make the banner the only way changes are applied.
Explicit apply only
<Table
{items}
{columns}
enableLiveUpdates
autoApplyOnNavigation={false}
/>