Home/Data & State/Live Updates
Data & State

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.

0 transactions applied
Symbol
Desk
Price
Change
Volume
AAPL
Equities
$40.00
+0.00
1.0M
MSFT
Rates
$77.00
+0.00
1.7M
NVDA
Credit
$114.00
+0.00
2.5M
GOOGL
FX
$151.00
+0.00
3.2M
META
Equities
$188.00
+0.00
3.9M
AMZN
Rates
$225.00
+0.00
4.7M
TSLA
Credit
$262.00
+0.00
5.4M
AMD
FX
$299.00
+0.00
6.1M
NFLX
Equities
$336.00
+0.00
6.8M
JPM
Rates
$373.00
+0.00
7.6M
V
Credit
$410.00
+0.00
8.3M
JNJ
FX
$447.00
+0.00
9.0M
XOM
Equities
$484.00
+0.00
9.8M
WMT
Rates
$521.00
+0.00
10.5M
BAC
Credit
$558.00
+0.00
11.2M
MA
FX
$595.00
+0.00
12.0M
CRM
Equities
$632.00
+0.00
12.7M
ABBV
Rates
$669.00
+0.00
13.4M
All records loaded
18 / 18 records

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.

6 rows loaded · cap 25
Time
Service
Level
Message
17:31:12
billing
error
connection reset
17:31:12
billing
warn
retry scheduled
17:31:12
auth
warn
retry scheduled
17:31:12
inventory
info
rate limit hit
17:31:12
auth
warn
rate limit hit
17:31:12
inventory
error
cache miss
All records loaded
6 / 6 records

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

PropertyTypeDefaultDescription
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.trackBystring | (row: T) => any—Required. How update and remove match rows. Without it the grid warns once and the transaction matches nothing.
config.live.flashCellsbooleanfalseAnimate changed cells: green ▲ on a numeric rise, red ▼ on a fall, neutral otherwise. Respects prefers-reduced-motion.
config.live.flashDurationnumber700Flash length in ms. Drives the CSS animation and the class removal together, so the two cannot disagree.
config.live.flashColumnsstring[]— (all)Restrict flashing to these column keys — the ones a reader is actually watching.
config.live.maxRowsnumber— (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.