Live Updates
Push new prices, statuses or readings into a rendered grid without refetching it. One call — grid.applyTransaction({ update: [row] }) — updates the rows in place and flashes what moved, green for up and red for down.
A ticking grid
A reading arrives every second and lands in place: the value changes, the cell flashes its direction, and the row keeps its position. Sort by any column, reorder or resize them, page through — the ticks keep landing underneath.
Three instruments re-price every second. Watch a cell you are not looking for: the flash is how a reader notices a change in a grid they are not staring at.
Sort first, then let it tick
Sort by Price, then wait. Rows keep their position while their values change — the grid does not re-sort under the reader's pointer. That is deliberate: a table that reorders itself while you are reading it is unusable.
The counter is the output, not the timer
transactions applied counts (liveUpdate) emissions, not interval ticks. Pause
the ticks and it stops; that is the same event your app would use to drive a toast, a chart, or an
audit trail.
Adding and removing rows, with a cap
An event feed: every tick appends a row and maxRows evicts the oldest, so the grid stays bounded no matter how long the page is left open. Remove takes ids, so you can drop a row without holding on to it.
Every 1.6 seconds a new event lands at the top (addIndex: 0) and the row count climbs
to 25, then stops: live.maxRows: 25 evicts the oldest as new ones arrive. Leave the
page open — it stays at 25.
Remove by id
Remove oldest event calls applyTransaction({ remove: [id] }) with a
bare number. remove accepts rows or ids interchangeably, so dismissing a row never
requires keeping the object around.
Filter it while it fills
Type in the Service box or pick a Level and let the feed keep running. New rows keep arriving underneath the filter, and the ring buffer keeps counting them.
One call, and the row changes where it stands
A grid that has already rendered does not need refetching to show new data. Hand it a transaction — up to three lists in one object — and it applies them in place:
grid.applyTransaction({
update: [{ id: 7, price: 184.32, change: 1.24 }], // matched by trackBy
add: [newRow],
remove: [oldRow.id], // rows OR bare ids
addIndex: 0, // where adds land; default is the end
});
The call returns a TransactionResult describing what actually changed, and
(liveUpdate) fires with the same object.
trackBy is mandatory
update and remove match rows by stable id, so
config.trackBy has to name the id field. Without it, rows are matched by object
reference, which fails the moment a payload arrives as a fresh object — which is always. The grid
warns once if you call a transaction without it.
You own the subscription
The grid does not connect to your WebSocket, poll your API, or hold a socket open. Your component
does, and calls applyTransaction() with what arrives. That keeps reconnection,
back-off, authentication and teardown where your app can see them — and it means any source works:
a socket, server-sent events, a poll, a signal effect, a router event.
Cell flash — what moved, and which way
With live.flashCells: true a changed cell animates for
live.flashDuration ms (default 700): green with a ▲ when a numeric value rose, red
with a ▼ when it fell, neutral otherwise. live.flashColumns narrows it to the columns
worth watching, so a row whose every field changed does not light up end to end. Flash respects
prefers-reduced-motion and is a decoration only — the data lands either way.
Keeping a feed bounded
A feed left open all day grows until the tab does. live.maxRows makes the grid a ring
buffer: once an add pushes the loaded count past the cap, the oldest rows are dropped.
Set it to whatever a reader can actually scroll back through.
Transactions and an active filter
With filterApplyMode: 'client' the grid filters loaded rows itself, so an
update that pushes a row out of the active search or column filter makes it
disappear on its own, and one that brings a row in makes it appear
— no refetch. Under the default 'server' mode your data source owns filtering, so live
edits are not re-filtered until the next fetch (the grid warns once, so it is never a silent
disagreement).
Bursts
Each call applies immediately, which is what makes the change visible in the same frame — and means
a burst of 500 calls is 500 renders. Batch them yourself: collect messages for a frame and send one
transaction with 500 rows in its update list, which is one render — the same work the
grid would have done, decided by the code that knows how fast the messages arrive.
applyTransaction(), (liveUpdate) and the whole of cell flash need no
licence. applyTransactionAsync() asks the library to coalesce for you instead; on a
build without that engine it applies immediately, tells you once, and never drops the
transaction.
Live update API
| Property | Type | Default | Description |
|---|---|---|---|
| grid.applyTransaction(tx) | TransactionResult<T> | — | Applies add / update / remove immediately and returns what changed. The one call this page is about. |
| GridTransaction<T> | { add?, update?, remove?, addIndex? } | — | All three lists may appear together. remove takes rows or bare ids; addIndex chooses where adds land (default: the end). |
| config.trackBy | string | (row: T) => any | — | Required. How update and remove match rows. Without it the grid warns once and the transaction matches nothing. |
| config.live.flashCells | boolean | false | Animate changed cells: green ▲ on a numeric rise, red ▼ on a fall, neutral otherwise. Respects prefers-reduced-motion. |
| config.live.flashDuration | number | 700 | Flash length in ms. Drives the CSS animation and the class removal together, so the two cannot disagree. |
| config.live.flashColumns | string[] | — (all) | Restrict flashing to these column keys — the ones a reader is actually watching. |
| config.live.maxRows | number | — (unbounded) | Ring buffer: once an add pushes the loaded count past this, the oldest rows are evicted. |
| (liveUpdate) | TransactionResult<T> | — | Fires once per applied transaction with added, removed and updated — the last carrying a per-cell diff (colKey, oldValue, newValue, direction). |
| grid.applyTransactionAsync(tx) | void | — | Buffers the transaction for the next scheduled flush. Coalescing needs a licensed build — without one the transaction is applied immediately and the grid says so once, rather than dropping it. |