# Grouping + aggregation

Group by department, sum salaries, average performance, expand/collapse keys.

Uses `@svgrid/grid` (MIT). Category: Sorting & Grouping. Live demo: https://svgrid.com/demos/07-grouping-aggregation/

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

## About this example

Row grouping in the Svelte 5 data grid over 500 people. columnGroupingFeature buckets rows by department, country or both and renders a group row in their place, rowExpandingFeature expands and collapses each group, and showGroupingControls exposes Group by this column in the header menu; buttons above the grid switch the grouping through api.setGroupBy. enableRowSummaries adds a sticky footer that sums the salary column and counts the others across every filtered row.

The grid's built-in grouping pipeline buckets rows by one or more columns and renders a group row in their place. Aggregation here is computed in the demo (the engine resolves shared values per group; this component layers sum/avg on top for the visible "Salary" and "Performance" columns via the row-summary footer).

## Imports, features and API used

- Imports: `@svgrid/grid`, `../shared/seed`
- Table features registered: `rowSortingFeature`, `columnGroupingFeature`, `rowExpandingFeature`
- Columns: `department` (Department), `country` (Country), `firstName` (First name), `lastName` (Last name), `age` (Age), `salary` (Salary), `performance` (Performance)
- SvGridApi methods called: `api.setGroupBy()`

## Frequently asked questions

### How do I group rows by a column?

Register columnGroupingFeature and rowExpandingFeature, then call api.setGroupBy(['department']) or let users pick Group by this column from the header menu with showGroupingControls on. Pass several ids for multi-level groups.

### How do I show totals?

enableRowSummaries appends a sticky footer row that sums numeric columns and shows a count for the rest across every filtered row. For a rolled-up value inside each group header, set aggregate on the column, as the group aggregators demo shows.

### How do I expand or collapse every group?

Click a group row to toggle it, or call api.expandAllGroups() and api.collapseAllGroups() from a toolbar. Passing an empty array to api.setGroupBy removes the grouping altogether.

## Related documentation

- [Grouping & aggregation](https://svgrid.com/docs/help/grouping-aggregation.md)
- [Migrating from SVAR Svelte DataGrid](https://svgrid.com/docs/help/migrating-from-svar-svelte-datagrid.md)
- [Row spanning](https://svgrid.com/docs/help/rows/row-spanning.md)
- [SvGrid in Angular](https://svgrid.com/docs/help/web-components/angular.md)
- [`<sv-grid>` reference](https://svgrid.com/docs/help/web-components/sv-grid.md)

## Related articles

- [Grouping and Aggregation in a Svelte Data Grid](https://svgrid.com/blog/grouping-and-aggregation/) - How to group rows by one or more columns and show per-group sums, averages, and counts using SvGrid's columnGroupingFeature - with real code and the edge cases that actually bite.

## Source code (07-grouping-aggregation.svelte)

```svelte
<script lang="ts">
  /**
   * 07. Grouping + aggregation
   * --------------------------
   * The grid's built-in grouping pipeline buckets rows by one or more
   * columns and renders a group row in their place. Aggregation here is
   * computed in the demo (the engine resolves shared values per group; this
   * component layers sum/avg on top for the visible "Salary" and
   * "Performance" columns via the row-summary footer).
   */
  import {
    SvGrid,
    tableFeatures,
    rowSortingFeature,
    columnGroupingFeature,
    rowExpandingFeature,
    type GridColumns,
    type SvGridApi,
  } from '@svgrid/grid'
  import { makePeople, type Person } from '../shared/seed'

  const features = tableFeatures({
    rowSortingFeature,
    columnGroupingFeature,
    rowExpandingFeature,
  })

  const rows = makePeople(500)

  const columns: GridColumns<Person> = [
    { field: 'department', header: 'Department', editorType: 'text' },
    { field: 'country',    header: 'Country',    editorType: 'text' },
    { field: 'firstName',  header: 'First name', editorType: 'text' },
    { field: 'lastName',   header: 'Last name',  editorType: 'text' },
    { field: 'age',        header: 'Age',        editorType: 'number' },
    {
      field: 'salary',
      header: 'Salary',
      editorType: 'number',
      format: { type: 'currency', currency: 'USD', options: { maximumFractionDigits: 0 } },
    },
    { field: 'performance', header: 'Performance', editorType: 'number' },
  ]

  let api = $state<SvGridApi<typeof features, Person> | null>(null)
  let groupBy = $state<string[]>(['department'])

  function applyGroup(by: string[]) {
    groupBy = by
    api?.setGroupBy(by)
  }
</script>

<section class="flex flex-col flex-1 min-h-0 gap-3">
  <div class="flex flex-wrap items-center gap-2 text-sm shrink-0">
    <span class="font-medium">Group by:</span>
    <button
      onclick={() => applyGroup([])}
      class="gb-btn rounded border px-3 py-1 {groupBy.length === 0 ? 'is-on' : ''}"
    >None</button>
    <button
      onclick={() => applyGroup(['department'])}
      class="gb-btn rounded border px-3 py-1 {groupBy.join() === 'department' ? 'is-on' : ''}"
    >Department</button>
    <button
      onclick={() => applyGroup(['country'])}
      class="gb-btn rounded border px-3 py-1 {groupBy.join() === 'country' ? 'is-on' : ''}"
    >Country</button>
    <button
      onclick={() => applyGroup(['department', 'country'])}
      class="gb-btn rounded border px-3 py-1 {groupBy.join() === 'department,country' ? 'is-on' : ''}"
    >Department → Country</button>
    <span class="gb-hint ml-3">
      Click a group row to expand. The row-summaries footer aggregates totals.
    </span>
  </div>

  <div class="flex-1 min-h-0">
    <SvGrid responsive={true}
      columnResize
      data={rows}
      columns={columns}
      features={features}
      filterMode="menu"
      selectionMode="cell"
      showGroupingControls={true}
      enableInlineEditing={false}
      enableCellSelection={true}
      enableRowSummaries={true}
      rowHeight={36}
      containerHeight="100%"
      fitColumns={true}
      onApiReady={(next) => {
        api = next
        // Apply the initial group-by once the imperative API is available,
        // then auto-expand the first group so the data isn't hidden behind a
        // single closed row on first paint.
        next.setGroupBy(groupBy)
        queueMicrotask(() => next.setRowExpanded(`group_department_Engineering`, true))
      }}
    />
  </div>
</section>

<style>
  .gb-btn {
    border-color: var(--sg-border, #cbd5e1);
    background: var(--sg-bg, #fff);
    color: var(--sg-fg, #0f172a);
  }
  .gb-btn:hover { background: var(--sg-row-hover-bg, rgba(148, 163, 184, 0.12)); }
  .gb-btn.is-on {
    background: var(--sg-bg-subtle, var(--sg-header-bg, #e2e8f0));
    border-color: var(--sg-border, #cbd5e1);
  }
  .gb-hint { color: var(--sg-muted, #64748b); }
</style>
```

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