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.
A live, editable Svelte 5 data grid example from the SvGrid gallery (Columns). See the SvGrid documentation for the full API.
About this example
Two Svelte 5 data grids kept in lockstep with alignedGridGroup. Scroll one horizontally and the other follows, resize a column in either and the matching column resizes in both, which is what a budget versus actuals comparison or a totals grid under a body grid needs. Enough month columns force a horizontal scrollbar so the sync is visible.
Two independent grids that share the same alignedGridGroup: scroll one horizontally and the other follows; resize a column in either and the matching column resizes in both. Classic use - a "budget vs actuals" comparison, or a totals grid pinned under a body grid - where the columns must stay lined up. There are enough month columns here to force a horizontal scrollbar so the sync is obvious.
Imports, features and API used
Imports: @svgrid/grid
Table features registered: rowSortingFeature
Frequently asked questions
How do I link two grids?
Give both the same alignedGridGroup string. Horizontal scroll and column widths are then synchronised between every grid in the group; sorting and filtering stay independent.
Do the grids need identical columns?
They need matching column ids for the columns that should stay aligned. A width change on a column id is applied to the same id in the other grid.
Can I align more than two grids?
Yes. Any number of grids can share a group; a body grid, a totals grid and a comparison grid can all move together.
Related documentation
Related articles
- Pinned (Frozen) Columns for Wide Svelte Data Grids - How to pin identity and action columns in SvGrid so users never lose their place while scrolling wide tables - declarative config, runtime API, and the tradeoffs that matter.
- 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.
Source code (182-aligned-grids.svelte)
<!-- Documented in: docs/help/columns/aligned-grids.md -->
<script lang="ts">
/**
* 182. Aligned grids
* ------------------
* Two independent grids that share the same `alignedGridGroup`: scroll one
* horizontally and the other follows; resize a column in either and the
* matching column resizes in both. Classic use - a "budget vs actuals"
* comparison, or a totals grid pinned under a body grid - where the columns
* must stay lined up. There are enough month columns here to force a
* horizontal scrollbar so the sync is obvious.
*/
import { SvGrid, tableFeatures, rowSortingFeature, type ColumnDef,
type GridColumns } from '@svgrid/grid'
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
type Row = { category: string } & Record<string, number | string>
function makeRows(base: number, spread: number): Row[] {
const cats = ['Salaries', 'Marketing', 'Cloud', 'Travel', 'Office']
return cats.map((category, ci) => {
const r: Row = { category }
for (let m = 0; m < MONTHS.length; m += 1) {
r[MONTHS[m]!] = base + ((ci * 7 + m * 13) % spread) * 1000
}
return r
})
}
const budget = makeRows(20_000, 40)
const actuals = makeRows(18_000, 55)
const features = tableFeatures({ rowSortingFeature })
const money = { type: 'currency', currency: 'USD', options: { maximumFractionDigits: 0 } } as const
function cols(): GridColumns<Row> {
return [
{ field: 'category', header: 'Category', width: 150 },
...MONTHS.map(
(m): ColumnDef<typeof features, Row> => ({
field: m,
header: m,
width: 110,
align: 'right',
cellDataType: 'number',
format: money,
}),
),
]
}
// Each grid needs its own column array instance.
const budgetCols = cols()
const actualCols = cols()
</script>
<section class="flex flex-col flex-1 min-h-0 gap-3">
<p class="text-sm shrink-0" style="color: var(--sg-fg);">
<strong>Scroll horizontally</strong> in either grid - the other follows.
<strong>Resize any month column</strong> - the matching column resizes in
both. Both grids share <code>alignedGridGroup="finance"</code>.
</p>
<div class="flex flex-col gap-3 flex-1 min-h-0">
<div class="ag-block">
<div class="ag-title">Budget</div>
<div class="aligned-pane">
<SvGrid responsive={true}
columnResize
data={budget}
columns={budgetCols}
features={features}
alignedGridGroup="finance"
selectionMode="none"
rowHeight={36}
containerHeight="100%"
/>
</div>
</div>
<div class="ag-block">
<div class="ag-title">Actuals</div>
<div class="aligned-pane">
<SvGrid responsive={true}
columnResize
data={actuals}
columns={actualCols}
features={features}
alignedGridGroup="finance"
selectionMode="none"
rowHeight={36}
containerHeight="100%"
/>
</div>
</div>
</div>
</section>
<style>
.ag-block { display: flex; flex-direction: column; min-height: 0; flex: 1; }
.ag-title {
font-size: 12px; font-weight: 700; text-transform: uppercase;
letter-spacing: 0.05em; color: var(--sg-muted, #64748b);
padding: 2px 2px 6px;
}
.aligned-pane { flex: 1; min-height: 0; }
</style>More Columns examples
- Column pinning + freezing - Wide 13-column grid. Pin Company left and Price right via the column menu; the middle scrolls under sticky edges.
- Columns hierarchy + manager - Side-panel tree of grouped columns: drag leaves to reorder, click a chevron to collapse a group into one summary column, toggle visibility per leaf or whole group.
- Column layout API - setColumnWidth + setColumnPinning + getColumnWidths + getColumnPinning. Save the snapshot to localStorage, restore on reload, drive widths and pins from buttons.
- Tool panel (Columns + Filters) - The docked enterprise sidebar, two tabs. Columns: toggle visibility, reorder up/down, group by a column. Filters: an operator + value control per column (numeric operators come free via cellDataType), kept in sync with the column menu. Enable with the toolPanel prop.
- Column reorder - Set enableColumnReorder on <SvGrid> and every header becomes draggable, with a drop indicator. api.setColumnOrder / getColumnOrder + onColumnOrderChange event; persist to restore across reloads.