Group aggregators
Declarative per-column rollups for group rows via the aggregate column option: sum, avg, min, max, count, countDistinct, extent, first, or a custom (values, rows) reducer. Each rollup is formatted with the column format and shown in the group header.
A live, editable Svelte 5 data grid example from the SvGrid gallery (Sorting & Grouping). See the SvGrid documentation for the full API.
About this example
Declarative per-column rollups for group rows in the Svelte 5 data grid. Set aggregate on a column and its group header shows the value formatted with the column's own format: sum for revenue with a currency format, avg for win rate as a percent, count, min, max, countDistinct, extent, first, or a custom (values, rows) reducer such as a median. The groupable shortcut switches grouping on.
Declarative per-column aggregation for group rows. Set aggregate on a column and the group header shows the rolled-up value, formatted with the column's own format:
{ field: 'revenue', aggregate: 'sum', format: { type: 'currency' } } { field: 'winRate', aggregate: 'avg', format: { type: 'percent' } } { field: 'score', aggregate: (vals) => median(vals) } // custom
Built-ins: sum, avg, min, max, count, countDistinct, extent, first - plus any custom (values, rows) => value reducer.
Imports, features and API used
Imports: @svgrid/grid
Table features registered: columnGroupingFeature
Columns: region (Region), rep (Rep), revenue (Revenue), deals (Deals), winRate (Win rate), score (Median score)
Frequently asked questions
Which aggregators are built in?
sum, avg, min, max, count, countDistinct, extent and first. Set one as the column's aggregate and the group row displays it through the column's format, so a currency column shows a currency total.
How do I write a custom aggregator?
Pass a function: aggregate: (values, rows) => median(values). It receives the leaf values of the group and the row objects, and its return value is rendered like a built-in.
Do aggregates nest for multi-level groups?
Yes. Each group level is rolled up from its own leaf rows, so a region total and a rep subtotal both show correctly when grouped by region then rep.
Related documentation
Related articles
- Building a Project / Task Board with a Svelte Data Grid - How to build a task management grid with grouping by status or assignee, inline edits, subtask tree rows, and saved views - without reaching for a dedicated project management tool.
- 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.
- Aggregation Functions Explained (Sum, Avg, Min, Max, Count) - A practical look at SvGrid's five built-in aggregations - what they compute, where the math quietly breaks, and how to handle the server-paged case where client totals are meaningless.
Source code (142-group-aggregators.svelte)
<!-- Documented in: docs/help/grouping/aggregators.md -->
<script lang="ts">
/**
* 142. Group aggregators
* ----------------------
* Declarative per-column aggregation for group rows. Set `aggregate` on a
* column and the group header shows the rolled-up value, formatted with the
* column's own `format`:
*
* { field: 'revenue', aggregate: 'sum', format: { type: 'currency' } }
* { field: 'winRate', aggregate: 'avg', format: { type: 'percent' } }
* { field: 'score', aggregate: (vals) => median(vals) } // custom
*
* Built-ins: sum, avg, min, max, count, countDistinct, extent, first -
* plus any custom (values, rows) => value reducer.
*/
import {
SvGrid,
tableFeatures,
columnGroupingFeature,
type GridColumns,
type SvGridApi,
} from '@svgrid/grid'
const features = tableFeatures({ columnGroupingFeature })
type Row = {
id: number
rep: string
region: string
revenue: number
deals: number
winRate: number
score: number
}
let seed = 0xa11ce
const rnd = () => ((seed = (seed * 1103515245 + 12345) >>> 0) / 0xffffffff)
const REGIONS = ['Americas', 'EMEA', 'APAC']
const NAMES = ['Ada', 'Grace', 'Alan', 'Margaret', 'Linus', 'Donald', 'Brian', 'Dennis', 'Barbara', 'Ken', 'Edsger', 'Tim']
const rows: Row[] = NAMES.flatMap((name, i) =>
REGIONS.map((region, j) => ({
id: i * 3 + j,
rep: name,
region,
revenue: Math.round(20_000 + rnd() * 200_000),
deals: Math.round(2 + rnd() * 40),
winRate: Math.round(rnd() * 100) / 100,
score: Math.round(rnd() * 100),
})),
)
const median = (vals: number[]): number => {
if (!vals.length) return 0
const s = [...vals].sort((a, b) => a - b)
return s[Math.floor(s.length / 2)]!
}
const columns: GridColumns<Row> = [
{ field: 'region', header: 'Region', width: 140 },
{ field: 'rep', header: 'Rep', width: 130 },
{
field: 'revenue',
header: 'Revenue',
width: 150,
aggregate: 'sum',
format: { type: 'currency', currency: 'USD', options: { maximumFractionDigits: 0 } },
},
{ field: 'deals', header: 'Deals', width: 110, aggregate: 'sum', align: 'right' },
{
field: 'winRate',
header: 'Win rate',
width: 120,
aggregate: 'avg',
align: 'right',
format: { type: 'percent' },
},
{
field: 'score',
header: 'Median score',
width: 130,
aggregate: median,
align: 'right',
},
]
let api = $state<SvGridApi<typeof features, Row> | null>(null)
</script>
<section class="flex flex-col flex-1 min-h-0 gap-3">
<div
class="shrink-0 rounded-lg border px-4 py-3"
style="border-color: var(--sg-border); background: var(--sg-header-bg);"
>
<p class="text-sm font-semibold" style="color: var(--sg-fg);">
Per-column rollups via <code>aggregate</code>
</p>
<p class="mt-1 text-xs" style="color: var(--sg-muted);">
Grouped by Region. Revenue + Deals = sum, Win rate = avg, Median score =
a custom reducer. Each rollup is formatted with its column's
<code>format</code> and shown in the group header. Use the column menu to
group by another field.
</p>
</div>
<div class="flex-1 min-h-0">
<SvGrid responsive={true}
columnResize
data={rows}
columns={columns}
features={features}
groupable
selectionMode="none"
rowHeight={36}
containerHeight="100%"
fitColumns={true}
onApiReady={(a) => {
api = a
queueMicrotask(() => a.setGroupBy(['region']))
}}
/>
</div>
</section>More Sorting & Grouping examples
- Grouping + aggregation - Group by department, sum salaries, average performance, expand/collapse keys.
- Group panel (drag & drop) - A Group Panel: drag chips into the panel to group, drag inside to reorder grouping levels, × to ungroup. Drives api.setGroupBy() under the hood.
- Group display modes + footers - Switch between groupRows banners, a single combined Group column, and one column per grouped field. groupFooters closes each group with a subtotal row under the real columns. Paging counts DATA rows, so pageSize means what it says: a page reprints the banners its rows sit under, and footers never eat the budget.
- Tree data (hierarchy) - treeData nests rows by parent id into an expandable hierarchy. Tree rows stay real data rows - own cells, formatting, editing - and just gain an expander plus indent. Takes flat parent-id data directly, or nested children arrays via flattenTreeData. Full treegrid a11y with arrow-key expand.
- Reporting workspace - Pivot-lite: group-by chips, per-column aggregator picker, saved views with localStorage persistence, live KPI strip + summary cards.