Grouping

The grouping transforms underneath groupBy, groupExpansion and the group-by bar.

Last updated November 3, 2026

groupRows reshapes filtered, sorted rows into nested groups, and the other functions here change the two pieces of state it reads: the group-by stack and which groups are collapsed. The view pipeline and the controller call them for you; reach for them directly in a custom adapter or group-by bar.

A GroupByState is readonly GroupByEntry[], outer level first, each entry { columnId, direction? } — a level's own direction orders its group values, not the rows inside a group. A GroupExpansionState is readonly string[]: the ids of the groups collapsed, so a group absent from it is expanded.

Grouping rows#

FunctionTypeDescription
groupRows(rows, groupBy, expansion, columns)readonly DisplayRow<Row>[]rows regrouped into groupBy's levels: a header per distinct value at each level, ordered with compareValues, then its nested groups or data rows, depth first. A collapsed group contributes its header only. Returns rows itself when groupBy is empty or names only absent columns.
groupRowId(path)stringA group's id, from the values of its levels outer to inner. Build ids with this, never by hand.

Filter and sort run first — resolveShownRows does both — and grouping keeps their order within each group. rowIndex is renumbered across the output, headers included.

A DisplayRow<Row> is a data row (ResolvedRow<Row>, with no kind), a group header (ResolvedGroupRow, kind: "group"), or a group's summary row (ResolvedGroupSummaryRow, kind: "group-summary", only from withGroupAggregates). Tell them apart with "kind" in entry, then entry.kind. A ResolvedGroupRow carries:

FieldTypeDescription
groupIdstringIts id — groupRowId(path) — the entry GroupExpansionState holds when collapsed.
levelnumberNesting depth, 0 for a top-level group.
columnIdstringThe column this level groups by.
valueunknownThis level's value — the last entry of path.
pathreadonly unknown[]The value at every level from the top down to this one.
expandedbooleanWhether the group is open.
countnumberIts leaf rows, collapsed or not.
rowIndexnumberIts position among the rows as rendered.
datasetIndexnumberIts position in the whole grouped result, on any page.
aggregatesAggregateResultsIts own aggregate results, once withGroupAggregates has run; empty otherwise.

The group-by stack#

FunctionTypeDescription
moveGroupByBefore(groupBy, columnId, beforeColumnId)GroupByStatecolumnId placed in front of beforeColumnId, or at the end for null — inserted when it is not grouped yet, moved when it is, so a header dropped on the bar and a chip dragged within it both use it. Returns groupBy itself for a move that changes nothing.
movesGroupBy(groupBy, columnId, beforeColumnId)booleanWhether that move would change anything, for a drop indicator that promises only real moves.

A moved level keeps its entry, and so its direction. To add or remove one level, the controller's toggleGroupBy(columnId) appends a column or takes it out.

Expanding and collapsing#

FunctionTypeDescription
toggleGroupExpansion(expansion, groupId)GroupExpansionStategroupId collapsed if it was open, opened if it was collapsed.
expandAllGroups(expansion)GroupExpansionStateEmpty — every group open. Returns expansion itself when already empty.
collapseAllGroups(rows)GroupExpansionStateThe id of every group header in rows, an already-grouped display. A group hidden under a collapsed one is not in rows, so it keeps its own state for when its parent reopens.

The group-by bar#

groupByBarVisibility takes a GroupByBarVisibility — "auto" (the default) shows the bar while anything is grouped or a header that can be dropped on it is being dragged, "always" shows it regardless, and "never" hides it.

In the React grid#

groupableColumns, groupByDraggableColumns and defaultGroupBy turn grouping on — see row grouping.

Edit this page on GitHub