# 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.

Uses `@svgrid/grid` (MIT). Category: Master-Detail & Forms. Live demo: https://svgrid.com/demos/70-multi-grid-sync/

Install: `npm i @svgrid/grid` (Svelte 5).

## 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

- [Aligned grids](https://svgrid.com/docs/help/columns/aligned-grids.md)

## Related articles

- [Building an Order Management Dashboard in Svelte](https://svgrid.com/blog/order-management-dashboard/) - 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](https://svgrid.com/blog/lazy-loading-master-detail-content/) - 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](https://svgrid.com/blog/building-grouping-trees-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)

```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>
```

[View this example on GitHub](https://github.com/sv-grid/sv-grid/blob/main/examples/src/demos/70-multi-grid-sync.svelte)
