Skip to content

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.
Interactive example

Row and column drill

Expand/collapse grouped rows; initial state; compact/tabular variants

The example loads when visible.

Row drill-down is enabled when:

  • the effective row hierarchy has more than one level
  • collapsed is a boolean, expanded state exists, or rowAxisLayout is 'compact'
const pivot: PivotConfig = {
rows: ['region', 'rep'],
values: [{ prop: 'sales', aggregator: 'sum' }],
collapsed: true,
};

Behavior:

  • collapsed: true starts grouped rows collapsed
  • collapsed: false starts grouped rows expanded
  • expanded stores 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.

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:

  • groupAggregations currently 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 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: false or { enabled: false } to disable column drill-down
  • set columnCollapse: { collapsed: false } to start with all generated groups expanded
  • use columnLevels to 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].subtotalValues can 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.

  • collapsed: row drill-down default
  • expanded: persisted row group expansion state
  • groupAggregations: render aggregate values inside grouped client-side Pivot rows
  • groupLabelColumn: choose where grouped row labels render ('grouped' or 'firstVisible')
  • columnCollapse: enable and configure generated column collapse
  • columnLevels: configure each generated column hierarchy level
  • collapsedColumns: persisted generated column collapse state

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.

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.

  • Expecting row drill-down with only one row field.
  • Expecting grouped rows to show metric values without enabling groupAggregations.
  • Expecting columnCollapse to work without generated column groups.
  • Treating expanded as UI row indexes. It is persisted grouped state, not viewport position.

Continue with Sorting And Filtering or Plugin Lifecycle.

Interactive example

Query original records

Select leaf/subtotal/total/Other cell and page original facts with projection

The example loads when visible.

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 match null. Values-on-rows uses the analytical row fields without a value-label pseudo-field; dataIndex identifies 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.
  • customColumns projects 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.