Aligned grids

Aligned grids are two or more independent <SvGrid> instances kept in lockstep: horizontal scroll and column-resize widths propagate between them. Use it for a budget-vs-actuals comparison, or a totals grid pinned under a body grid, where the columns must stay lined up.

Give every grid in the set the same non-empty alignedGridGroup:

<SvGrid data={budget}  columns={budgetCols}  alignedGridGroup="finance" />
<SvGrid data={actuals} columns={actualCols} alignedGridGroup="finance" />

Open the live example: Aligned grids (Columns)

What syncs

Vertical scroll and selection stay independent (each grid owns its own rows).

Notes

Two grids, one group

Scroll either grid sideways and the other follows; drag a column edge and the matching column resizes in both. Nothing is shared but the group string - the two grids have their own data, their own selection and their own vertical scroll.

<script lang="ts">
  import { SvGrid, type GridColumns } from '@svgrid/grid'

  type Quarter = { line: string; q1: number; q2: number; q3: number; q4: number }

  const budget: Quarter[] = [
    { line: 'Salaries',  q1: 420000, q2: 430000, q3: 435000, q4: 440000 },
    { line: 'Cloud',     q1: 38000,  q2: 41000,  q3: 44000,  q4: 47000 },
    { line: 'Travel',    q1: 12000,  q2: 15000,  q3: 9000,   q4: 18000 },
  ]

  const actuals: Quarter[] = [
    { line: 'Salaries',  q1: 418200, q2: 433100, q3: 431900, q4: 447500 },
    { line: 'Cloud',     q1: 40100,  q2: 39800,  q3: 46600,  q4: 52300 },
    { line: 'Travel',    q1: 9400,   q2: 17600,  q3: 8100,   q4: 21200 },
  ]

  const money = { type: 'currency', currency: 'USD', options: { maximumFractionDigits: 0 } } as const

  // Each grid gets its OWN column array instance, matched by id/field.
  const cols = (): GridColumns<Quarter> => [
    { field: 'line', header: 'Line item', width: 160 },
    { field: 'q1', header: 'Q1', width: 130, format: money },
    { field: 'q2', header: 'Q2', width: 130, format: money },
    { field: 'q3', header: 'Q3', width: 130, format: money },
    { field: 'q4', header: 'Q4', width: 130, format: money },
  ]
</script>

<p>Budget</p>
<SvGrid data={budget} columns={cols()} alignedGridGroup="finance" containerHeight={150} />

<p>Actuals</p>
<SvGrid data={actuals} columns={cols()} alignedGridGroup="finance" containerHeight={150} />

A totals strip under the body

The common shape: a tall body grid and a one-row grid beneath it carrying the totals. Same group, same column ids, so the totals stay under the right numbers however far the body is scrolled.

<script lang="ts">
  import { SvGrid, type GridColumns } from '@svgrid/grid'

  type Quarter = { line: string; q1: number; q2: number; q3: number; q4: number }

  const rows: Quarter[] = [
    { line: 'Salaries', q1: 420000, q2: 430000, q3: 435000, q4: 440000 },
    { line: 'Cloud',    q1: 38000,  q2: 41000,  q3: 44000,  q4: 47000 },
    { line: 'Travel',   q1: 12000,  q2: 15000,  q3: 9000,   q4: 18000 },
    { line: 'Hardware', q1: 61000,  q2: 22000,  q3: 18000,  q4: 74000 },
  ]

  const sum = (k: 'q1' | 'q2' | 'q3' | 'q4') => rows.reduce((s, r) => s + r[k], 0)

  const totals: Quarter[] = [
    { line: 'Total', q1: sum('q1'), q2: sum('q2'), q3: sum('q3'), q4: sum('q4') },
  ]

  const money = { type: 'currency', currency: 'USD', options: { maximumFractionDigits: 0 } } as const

  const cols = (): GridColumns<Quarter> => [
    { field: 'line', header: 'Line item', width: 160 },
    { field: 'q1', header: 'Q1', width: 130, format: money },
    { field: 'q2', header: 'Q2', width: 130, format: money },
    { field: 'q3', header: 'Q3', width: 130, format: money },
    { field: 'q4', header: 'Q4', width: 130, format: money },
  ]
</script>

<SvGrid data={rows} columns={cols()} alignedGridGroup="totals-demo" containerHeight={170} sortable />

<SvGrid data={totals} columns={cols()} alignedGridGroup="totals-demo" containerHeight={80} />

See also

Live examples

  • Aligned grids - Two independent grids sharing alignedGridGroup stay in lockstep: scroll one horizontally and the other follows; resize a column in either and the matching column resizes in both. Budget vs actuals comparison.
  • 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.

Related articles

  • Bundle Size of Svelte Data Grids - How to Compare - README bundle numbers are nearly useless. Here is how to measure the real delta a data grid adds to your Svelte app, and why feature-gated architectures change the math entirely.
  • Open-Source vs Commercial Svelte Data Grids - A practical breakdown of licensing trade-offs for Svelte data grids - what open-source actually costs you, what commercial actually buys you, and how to think about total cost of ownership before you commit.
  • Debounce vs Throttle (for Grids and Beyond) - Debounce and throttle are not interchangeable. Here is when each one belongs in your data grid, with real code for filter inputs, live feeds, and scroll.