the realtime layer
The realtime layer for your data grid.
The screen a live desk watches is never one table. It is several related views moving at once because the same events are landing behind them. The Data Router lets you build that screen from a single feed: point it at one stream, partition it by a property, and each grid and chart fills itself with only its own slice, live. No connection per panel, and no one giant grid filtered to death.
Open the full demo → Read the walkthrough → Build the ops console →
why it is worth the layer
One source of truth, kept in one place.
When a screen has to show several related things from one live source, the shortcut is usually one of two shapes, and both go wrong in the same week.
A connection per panel
Each table and chart opens its own socket, so you are running several feeds of the same truth. They drift: one reconnects and the others do not, one lags a few seconds, and the numbers on the screen stop adding up because they were read at different moments.
One giant grid, filtered six ways
A single table holds everything and gets sliced to pull each view out of it. It is slow to paint, awkward to sort, and impossible to reason about, because a change to one view is a change to the same object every other view is reading.
The Data Router takes the other path. One stream comes in, one router owns the routing, and the views behind it stay plain grids and charts that only know how to show the rows they are given. The picture on the screen always adds up, because every panel is reading the same tick of the same feed.
a router in front, plain views behind
The wiring reads the way the screen looks.
You build plain grids that only know how to show rows, and you put one router in front of
them. createDataRouter takes the property to partition on and the
row key; attach points each view at the slice it should receive.
load fans one opening snapshot across every view, and
apply delivers each record in a mixed live batch only to the views
it belongs in, through each grid's own keyed update path.
link is the one connection you usually do want: pick a run of rows
in one grid and a linked view narrows to the related rows on a shared field. A headless grid
feeding a chart is just another view, so a live chart drops into the same call.
The router is a separate, opt-in module with a global of its own. It changes nothing in the grid core and composes with the charts, streaming and statistics modules, so the whole screen is a system rather than a grid.
<!-- the grid and the data router, as globals -->
<script src="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid/lattice-grid.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid/modules/data-router.umd.js"></script>
<script>
var g = LatticeGrid;
var createDataRouter = LatticeGridDataRouter.createDataRouter;
// Plain views. None of them knows about the feed, or about each other.
var orders = g.createGrid(document.querySelector('#orders'), { rowKey: 'id', columns: orderCols });
var returns = g.createGrid(document.querySelector('#returns'), { rowKey: 'id', columns: returnCols });
var metrics = g.createHeadlessGrid({ rowKey: 'ts', columns: metricCols });
// One router. Partition the feed on "type", then attach each view to its slice.
var router = createDataRouter({ key: 'type', rowKey: 'id' });
router.attach(orders, 'order');
router.attach(returns, 'return');
router.attach(metrics, 'metric', { rowKey: 'ts' });
// Pick a run of orders and the returns grid narrows to the same regions.
router.link(orders, returns, { from: 'region', to: 'region' });
// The opening snapshot, split across every attached view in one call.
router.load(snapshot);
// The live feed: a mixed batch each tick, each record delivered only where it belongs.
socket.onmessage = function (e) { router.apply(JSON.parse(e.data)); };
</script>
the same shape, four desks
One feed, many views, wherever the desk changes.
Once the feed and the views are separate, the same pattern fits screens that look nothing alike. The desk changes and the partition key changes; the code does not.
You run an operations console
Orders, shipments and incidents are all arriving on one stream, and the wall needs a table for each plus a throughput line along the bottom. Split the stream by what each record is and every panel fills itself, so the person watching gets one coherent picture that always adds up.
You run a trading terminal
A blotter of fills, a positions grid and a depth ladder are the same market feed seen three ways. Route the ticks by what each one is and the three views each take their share of every message, with no separate subscriptions fighting to stay in step.
You run an IoT fleet
Telemetry from a whole fleet comes up one pipe, and the control room wants a lane per device class with its own grid and a trend beside it. Partition the single feed by class and every lane fills itself, so adding a class is adding a grid, not adding a connection.
You run a support desk
One ticket stream, and the team wants a board with a lane per queue so nobody scrolls to find their work. Split the stream by queue and each lane is a plain grid of just its tickets, live, off the one source everyone is already on.
what the router handles for you
The part that used to rot, owned in one place.
Partitioned by property
One feed is split by a property you name, and each grid or chart only ever sees its own slice. A record that changes which slice it belongs to moves rather than duplicating.
Updates in place, state kept
Changes land as a keyed diff on the one row they touch, so every view keeps its scroll, selection, sort and filter while the feed keeps arriving. Only the affected view repaints.
Nothing dropped silently
A record that matches no view is counted and surfaced, not thrown away, so a feed that grows a new kind of event tells you rather than losing it without a trace.
Cross-grid selection
Pick a row in one grid and a linked view narrows to the related rows on a shared field, so a reader drills from one view into another with a single click while the feed runs underneath.
Grids and charts alike
A headless grid feeding a chart is just another attached view, so a live line or bar takes its slice of the same feed the tables do, with no extra plumbing.
A separate, opt-in module
The router loads only where a page needs it, changes nothing in the grid core, and composes with the charts, streaming and statistics modules. A page that never routes never pays for it.
Because the router applies plain JavaScript changes from any source, you can drive the whole screen from a mock feed and build the real thing before the backend exists. The operations console tutorial does exactly that, then swaps one line to go live.
Build the screen on localhost, free.
Development on localhost needs no licence key, so you can wire the router to your feed and see the real screen move before you decide anything. Licence per domain when you ship.
Operations console tutorial The walkthrough Documentation Open in the sandbox