Drill-Down
Drill-down lets users move between summary and detail inside the Pivot structure. In the current implementation there are two separate drill-down concepts: grouped row expansion and generated column collapse.
Open the full demo and framework source.Row Drill-Down
Section titled “Row Drill-Down”Row and column drill
Expand/collapse grouped rows; initial state; compact/tabular variants
Row drill-down is enabled when:
- the effective row hierarchy has more than one level
collapsedis a boolean,expandedstate exists, orrowAxisLayoutis'compact'
const pivot: PivotConfig = { rows: ['region', 'rep'], values: [{ prop: 'sales', aggregator: 'sum' }], collapsed: true,};Behavior:
collapsed: truestarts grouped rows collapsedcollapsed: falsestarts grouped rows expandedexpandedstores per-group overrides using grouped path keys- active row drill-down uses one compact Rows column by default
rowAxisLayout: 'tabular'keeps one visible column per configured row field
Compact mode changes only the generated grid columns. The Pivot model, field
panel, persisted configuration, and remote requests continue to use separate
region and rep dimensions.
const pivot: PivotConfig = { rows: ['region', 'rep'], values: [{ prop: 'sales', aggregator: 'sum' }], collapsed: true, rowAxisLayout: 'tabular', // opt back into separate Region and Rep columns};PivotPlugin translates the Pivot row structure into RevoGrid grouping options, then emits pivot-config-update when users expand or collapse groups.
Grouped Row Aggregates
Section titled “Grouped Row Aggregates”Set groupAggregations: true to render aggregate values inside grouped Pivot rows.
const pivot: PivotConfig = { rows: ['region', 'rep'], columns: ['year'], values: [{ prop: 'sales', aggregator: 'sum' }], collapsed: true, groupAggregations: true,};Behavior:
- the grouped row still shows the expand/collapse UI and group label
- generated Pivot value columns show the aggregate for the current grouped branch
- nested groups aggregate only their own descendants
- collapsed groups still show their aggregate values
If you hide the grouped row field column (for example with hide-columns="region"), you can move the grouped label into the first visible row cell:
const pivot: PivotConfig = { rows: ['region', 'product'], columns: ['quarter'], values: [{ prop: 'revenue', aggregator: 'sum' }], collapsed: true, rowAxisLayout: 'tabular', groupAggregations: true, groupLabelColumn: 'firstVisible',};groupLabelColumn modes:
'grouped'(default): keep label in the grouped field column'firstVisible': render label in the first currently visible row cell
Current limitation:
groupAggregationscurrently applies to the client-side Pivot path- server-side Pivot still renders grouped labels only unless backend-precomputed grouped aggregates are added
Column Drill-Down
Section titled “Column Drill-Down”Column drill-down is built into Pivot whenever columns is non-empty. Generated
groups start collapsed without additional configuration.
columns: ['year', 'quarter', 'month']Behavior:
- generated Pivot column groups become collapsible
- generated groups start collapsed at the highest level and expand one level at a time
- set
columnCollapse: falseor{ enabled: false }to disable column drill-down - set
columnCollapse: { collapsed: false }to start with all generated groups expanded - use
columnLevelsto override collapse, subtotal, filter, and sort behavior independently at each zero-based column level - collapsed groups render a placeholder header/cell
- placeholder cells use a collapsed-bucket aggregator, not a naive sum of visible child cells
- expanded subtotal columns appear before descendant columns by default, keeping the parent aggregate visible and actionable
columnLevels[level].subtotalValuescan keep that aggregate to one value field—or any ordered subset—when the Pivot has multiple measures- expanding reveals one level only; descendants and sibling branches keep their current state
- aggregate filters and sorting remain attached to the same analytical group when its compact and expanded presentations switch
Persisted state for generated groups lives in collapsedColumns.
See Column Totals With Drill-Down for the complete subtotal/collapse behavior matrix, precedence rules, and screenshots.
Related Options
Section titled “Related Options”collapsed: row drill-down defaultexpanded: persisted row group expansion stategroupAggregations: render aggregate values inside grouped client-side Pivot rowsgroupLabelColumn: choose where grouped row labels render ('grouped'or'firstVisible')columnCollapse: enable and configure generated column collapsecolumnLevels: configure each generated column hierarchy levelcollapsedColumns: persisted generated column collapse state
Placeholder Behavior
Section titled “Placeholder Behavior”When a column group is collapsed, Pivot can render:
- a default placeholder header based on the group name
- a custom placeholder header or cell template
- a custom collapsed aggregator, globally or per value field
This is useful when a collapsed analytical bucket should still show a meaningful aggregate instead of just hiding all child columns.
State Persistence
Section titled “State Persistence”pivot-config-update announces a synchronous, cancelable proposal. Persisting its payload can save a change that later fails or is superseded. Subscribe to pivotPlugin.getAuthoringController() and save each newly committed snapshot whose status is ready; snapshot.revision prevents duplicate writes. The accepted report includes drill state. See the complete persistence subscription and portable report export for runtime bindings and restore behavior.
Common Mistakes
Section titled “Common Mistakes”- Expecting row drill-down with only one row field.
- Expecting grouped rows to show metric values without enabling
groupAggregations. - Expecting
columnCollapseto work without generated column groups. - Treating
expandedas UI row indexes. It is persisted grouped state, not viewport position.
Continue with Sorting And Filtering or Plugin Lifecycle.
Query Original Records
Section titled “Query Original Records”Query original records
Select leaf/subtotal/total/Other cell and page original facts with projection
PivotPlugin.loadPivotDrilldown queries the original client source without requiring
engine.mode. A custom adapter or remote store receives the same request instead.
Use semantic axis values, rather than rendered labels or generated property keys:
const page = await pivotPlugin.loadPivotDrilldown({ cell: { rowPath: ['North'], columnPath: [2025], dataIndex: 0 }, customColumns: ['orderId', 'sales'], offset: 0, limit: 50, signal: abortController.signal,});// page.data contains this page; page.totalCount counts all matching records.For the built-in client engine:
- Paths are typed prefixes in the configured axis order.
[]means the total for that axis; a shorter path addresses a subtotal. Missing source values matchnull. Values-on-rows uses the analytical row fields without a value-label pseudo-field;dataIndexidentifies the configured measure instance. - Active member selections, independent chart filter contributions and Core source predicates constrain the facts. Aggregate result filters do not become source-record predicates. Opaque custom Core evaluators are rejected when an exact source predicate cannot be evaluated.
- Top/Bottom N uses the report’s ranking. An Other cell returns its original transactions and labels, in original source order. Hidden ranked-out members contribute no facts when the report excludes Other.
customColumnsprojects known source/configured fields. Omit it to return all source properties; an empty array returns empty record objects with the count. Applications decide which fields to expose. This local projection is not an authorization boundary.- Offset must be a nonnegative safe integer and limit a positive safe integer. Invalid selectors, measure indices and paths reject instead of widening the query. Already-aborted requests reject.
For calculated measures, records describe the selected row/column coordinate. They do not enumerate every operand of a running total, ratio or other post-aggregation formula, and summing them need not reproduce the displayed calculation.
loadPivotFilterValues also works in default local mode. Its domain excludes the
requested field’s own Pivot member selection while retaining Core source filters,
other fields and independent filter owners. Search runs before distinct-value
paging; missing values are exposed as null.
Standalone ClientPivotEngineAdapter queries use the constructor’s source and
configuration. Its filterValues request additionally accepts a portable filter
tree. Comparisons preserve primitive types, text predicates are case-sensitive,
and unsupported or malformed predicates reject. Grid Core predicates are
available through PivotPlugin, which owns that state.