Multi-grid sync
Two grids over one $state array - edits on the left propagate to the right instantly; each grid keeps its own filter + sort.
A live, editable Svelte 5 data grid example from the SvGrid gallery (Master-Detail & Forms). See the SvGrid documentation for the full API.
About this example
Two Svelte 5 data grids over a single $state array. Edit a cell in either grid and the other re-renders immediately because both read the same reactive reference, with no grid-to-grid wiring. Filter and sort state stay per grid, so each keeps its own view of the shared inventory rows.
Two grids over a single Svelte 5 $state array. Edit a cell in either - the other re-renders immediately because both grids read the same reactive reference.
No grid-to-grid wiring is needed; this is the natural Svelte pattern. The demo also shows that filter / sort state is per-grid (each grid's view is independent) while the data they project is shared.
Imports, features and API used
Imports: @svgrid/grid
Table features registered: rowSortingFeature, columnFilteringFeature
Columns: sku (SKU), product (Product), warehouse (Warehouse), qty (Qty), reorder (Reorder pt)
Frequently asked questions
How do the grids stay in sync?
They are given the same $state array as data. An inline edit in one grid mutates the row object, and Svelte's reactivity re-renders the other grid's cell; nothing else is needed.
Why do the filters not affect both grids?
Sorting and filtering are internal grid state, held per instance. Each grid projects the shared rows through its own pipeline, so one can show a filtered subset while the other shows everything.
Does this work with the headless engine too?
Yes. Two createSvGrid engines given the same array behave the same way; the shared-state headless demo goes further and shares the sort state as well.
Related documentation
Related articles
- Building an Order Management Dashboard in Svelte - How to wire up an order management grid with master-detail line items, status workflows, server-side data, and bulk fulfillment actions using SvGrid.
- Lazy-Loading Master-Detail Content in SvGrid - Fetch detail-panel data only when a row is expanded, cache the results, and cancel abandoned requests - keeping a large grid fast without paying for panels no one views.
- Inside SvGrid: Grouping, Trees, and Master-Detail - Three different ways to show hierarchy in a data grid, unified under one expansion model in SvGrid - the design decision and how each feature actually works.
Source code (70-multi-grid-sync.svelte)
<script lang="ts">
/**
* 70. Multi-grid sync - shared $state
* ----------------------------------
* Two grids over a single Svelte 5 `$state` array. Edit a cell in
* either - the other re-renders immediately because both grids read
* the same reactive reference.
*
* No grid-to-grid wiring is needed; this is the natural Svelte
* pattern. The demo also shows that filter / sort state is per-grid
* (each grid's view is independent) while the data they project is
* shared.
*/
import {
SvGrid,
tableFeatures,
rowSortingFeature,
columnFilteringFeature,
type GridColumns,
} from '@svgrid/grid'
type Inventory = {
id: string
sku: string
product: string
warehouse: 'East' | 'West' | 'Central'
qty: number
reorder: number
}
let rows = $state<Inventory[]>([
{ id: 'i01', sku: 'A-100', product: 'Steel sheet 1/2"', warehouse: 'East', qty: 240, reorder: 100 },
{ id: 'i02', sku: 'A-101', product: 'Steel sheet 3/4"', warehouse: 'West', qty: 80, reorder: 100 },
{ id: 'i03', sku: 'B-200', product: 'Stainless rivets', warehouse: 'East', qty: 6800, reorder: 1000 },
{ id: 'i04', sku: 'B-201', product: 'Brass rivets', warehouse: 'Central', qty: 1200, reorder: 1500 },
{ id: 'i05', sku: 'C-300', product: 'Drill bit set', warehouse: 'West', qty: 44, reorder: 20 },
{ id: 'i06', sku: 'C-301', product: 'Impact driver', warehouse: 'Central', qty: 12, reorder: 10 },
{ id: 'i07', sku: 'D-400', product: 'Wire rope 1/4"', warehouse: 'East', qty: 60, reorder: 100 },
{ id: 'i08', sku: 'D-401', product: 'Wire rope 3/8"', warehouse: 'West', qty: 180, reorder: 100 },
])
const features = tableFeatures({ rowSortingFeature, columnFilteringFeature })
const columns: GridColumns<Inventory> = [
{ field: 'sku', header: 'SKU', editorType: 'text', width: 100 },
{ field: 'product', header: 'Product', editorType: 'text', width: 180 },
{ field: 'warehouse', header: 'Warehouse', editorType: 'list',
editorOptions: ['East', 'West', 'Central'],
width: 140,
},
{ field: 'qty', header: 'Qty', editorType: 'number', width: 100 },
{ field: 'reorder', header: 'Reorder pt',editorType: 'number', width: 130 },
]
</script>
<section class="flex flex-col flex-1 min-h-0 gap-3">
<p class="sync-note text-sm shrink-0">
Both grids read from the same <code>$state</code> array. Edit any
<code>Qty</code> or <code>Warehouse</code> on the left - the right
grid updates instantly. Each grid keeps its OWN filter + sort.
</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 flex-1 min-h-0">
<div class="flex flex-col gap-2 min-h-0">
<div class="sync-title text-sm font-medium">
Operations view <span class="sync-sub font-normal">- editable</span>
</div>
<div class="flex-1 min-h-0">
<SvGrid responsive={true}
columnResize
data={rows}
columns={columns}
features={features}
filterMode="menu"
selectionMode="cell"
showRowNumbers={false}
enableInlineEditing={true}
enableCellSelection={true}
rowHeight={32}
containerHeight="100%"
fitColumns={true}
/>
</div>
</div>
<div class="flex flex-col gap-2 min-h-0">
<div class="sync-title text-sm font-medium">
Analytics view <span class="sync-sub font-normal">- read-only mirror</span>
</div>
<div class="flex-1 min-h-0">
<SvGrid responsive={true}
columnResize
data={rows}
columns={columns}
features={features}
filterMode="menu"
selectionMode="cell"
showRowNumbers={false}
enableInlineEditing={false}
enableCellSelection={true}
enableRowSummaries={true}
rowHeight={32}
containerHeight="100%"
fitColumns={true}
/>
</div>
</div>
</div>
</section>
<style>
/* Caption chrome follows the active grid theme. */
.sync-note { color: var(--sg-muted, #64748b); }
.sync-title { color: var(--sg-fg, #334155); }
.sync-sub { color: var(--sg-muted, #64748b); }
</style>More Master-Detail & Forms examples
- Forms-in-grid (master/detail) - Master grid + tabbed detail form. Dirty tracking, inline validation, atomic Save / Discard, contact subgrid.
- Side-drawer edit form - Linear / Notion pattern: click a row, a polished drawer slides in with the full record, dirty-state badge, live validation, Esc / Ctrl+Enter shortcuts.
- Detail rows (expandable) - Stripe / GitHub-style inline row expansion: click the chevron to reveal a 4-panel detail (line items, shipping, payments, support thread).
- Master / detail (nested grid) - The classic master/detail: expand any account row to reveal a full nested SvGrid of its call records. Built on isDetailRow + renderDetailRow - a real full-width detail row hosting another grid.