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
- Horizontal scroll - scrolling one grid scrolls the others to the same
scrollLeft. - Column widths - resizing a column mirrors to the matching column (by
id) in every other grid in the group.
Vertical scroll and selection stay independent (each grid owns its own rows).
Notes
- Declare the same columns (matched by
id, falling back tofield) in each grid so widths map one-to-one. Give each grid its own column-array instance. - Only user resizes are mirrored; default and
fitColumnswidths already match when the columns and container widths match. - Any number of grids can share a group; add/remove one and the rest keep working. Groups are independent - use different strings for unrelated sets.
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
- Column sizing
- Multi-grid sync - sharing data (not layout) via
$state
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.