

## Overview [#overview]

Pivoting turns a flat record set into an analytical summary. Configure the view
with three familiar areas:

| Area    | Purpose                                             | Configure with              |
| ------- | --------------------------------------------------- | --------------------------- |
| Rows    | Group records into expandable categories.           | `grouping`                  |
| Columns | Split Values across each distinct dimension value.  | `pivoting.columnDimensions` |
| Values  | Aggregate measures such as amount, hours, or count. | `pivoting.values`           |

Use `DataGrid.PivotControl` as the standard user-facing control and compose it
into your product toolbar. Its compact icon trigger is the default; use
`compact={false}` when the toolbar should show the Pivot label and active field
count. Accessor columns are available under Columns by default and under Rows
when they also support grouping. Add `pivotAggregationFns` to a measure column
to make it available under Values.

Filters run before the grid groups and aggregates records. Prefer flat data and
low-cardinality Columns fields: each distinct Columns value creates a displayed
column. The consumer continues to own the records, toolbar, and persistence.
See [Shared State](/components/data-grid/shared-state) when another surface
needs to control pivot state and [Saved Views](/components/data-grid/saved-views)
when users need to save it with the rest of a grid view.

## Examples [#examples]

### Rows, Columns, and Values [#rows-columns-and-values]

Declare the aggregations each measure supports, then set the initial Rows,
Columns, and Values configuration.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="DataGridPivotingScenario" />
  </Tab>

  <Tab value="Code">
    ```tsx
    const columns = columnHelper.columns([
      columnHelper.accessor("date", {
        header: "Date",
        enableGrouping: true,
      }),
      columnHelper.accessor("feeEarner", { header: "Fee earner" }),
      columnHelper.accessor("hours", {
        header: "Hours",
        aggregationFn: "sum",
        pivotAggregationFns: ["sum", "mean", "count"],
      }),
    ]);

    const table = useDataGridTable(
      {
        columns,
        data: records,
        getRowId: (row) => row.id,
        initialState: {
          expanded: true,
          grouping: ["date"],
          pivoting: {
            columnDimensions: ["feeEarner"],
            values: [{ columnId: "hours", aggregationFn: "sum" }],
          },
        },
      },
      () => null,
    );

    <Toolbar>
      <Toolbar.Heading>
        <Toolbar.Title>Hours analysis</Toolbar.Title>
      </Toolbar.Heading>
      <Toolbar.Actions>
        <DataGrid.PivotControl table={table} />
      </Toolbar.Actions>
    </Toolbar>
    ```
  </Tab>
</Tabs>

### Summary-only views [#summary-only-views]

Turn off **Show source rows** in the pivot menu, or set
`pivoting.showSourceRows: false`, to show grouped summaries without their
contributing records. Groups can still expand into further summary levels;
the final summary level has no expansion arrow. Totals and filters are unchanged.
This preference is saved with the view, separately from expansion state.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="DataGridPivotSummariesScenario" />
  </Tab>

  <Tab value="Code">
    ```tsx
    initialState: {
      expanded: true,
      grouping: ["phase", "feeEarner"],
      pivoting: {
        columnDimensions: [],
        showColumnTotals: true,
        showSourceRows: false,
        values: [{ columnId: "amount", aggregationFn: "sum" }],
      },
    }
    ```
  </Tab>
</Tabs>

Source rows are shown by default. The setting also works with grouping alone;
ungrouped record views remain unchanged.

### Sorting and value display [#sorting-and-value-display]

Sort a Rows or Columns dimension by its own values or by an active aggregate.
Values can display their result directly or as a percentage of the row,
column, or grand total.

```tsx
pivoting: {
  columnDimensions: ["costCentre"],
  columnDimensionSortBy: {
    costCentre: { columnId: "amount", aggregationFn: "sum" },
  },
  columnDimensionSorting: { costCentre: "desc" },
  rowDimensionSortBy: {
    phase: { columnId: "amount", aggregationFn: "sum" },
  },
  values: [
    {
      columnId: "amount",
      aggregationFn: "sum",
      showAs: "percentOfRowTotal",
    },
  ],
}
```

Use `sortDimensionValues` when a dimension needs a domain-specific order that
cannot be expressed by its label or an aggregate.

### Overall values [#overall-values]

Set `columnScope: "overall"` when a Value should remain one standalone
aggregate instead of being split across the active Columns dimensions.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="DataGridOverallValuesScenario" />
  </Tab>

  <Tab value="Code">
    ```tsx
    pivoting: {
      columnDimensions: ["costCentre"],
      values: [
        { columnId: "amount", aggregationFn: "sum" },
        { columnId: "tax", aggregationFn: "sum", columnScope: "overall" },
        { columnId: "gross", aggregationFn: "sum", columnScope: "overall" },
      ],
    }
    ```
  </Tab>
</Tabs>

Overall Values always show their raw aggregate because percentage displays
require a Columns breakdown.

### Row totals [#row-totals]

Set `showRowTotals` to add one total for each Value that is split across
Columns.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="DataGridPivotRowTotalsScenario" />
  </Tab>

  <Tab value="Code">
    ```tsx
    pivoting: {
      columnDimensions: ["costCentre"],
      showRowTotals: true,
      values: [{ columnId: "amount", aggregationFn: "sum" }],
    }
    ```
  </Tab>
</Tabs>

### Column totals [#column-totals]

Set `showColumnTotals` to add filtered aggregate footers for the active Values.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="DataGridPivotColumnTotalsScenario" />
  </Tab>

  <Tab value="Code">
    ```tsx
    pivoting: {
      columnDimensions: ["costCentre"],
      showColumnTotals: true,
      values: [{ columnId: "amount", aggregationFn: "sum" }],
    }
    ```
  </Tab>
</Tabs>

### Multi-value dimensions [#multi-value-dimensions]

Use `getDimensionValues` when one source record belongs to several values of
the same dimension. Return a stable key, display label, and typed value for
each occurrence.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="DataGridMultiValueGroupingScenario" />
  </Tab>

  <Tab value="Code">
    ```tsx
    columnHelper.accessor("memberships", {
      header: "Reviewer",
      enableGrouping: true,
      getDimensionValues: (record) =>
        record.memberships.map((membership) => ({
          key: membership.id,
          label: membership.name,
          value: membership,
        })),
    });
    ```
  </Tab>
</Tabs>

Use `row.getDimensionValue<Membership>(columnId)` to access the occurrence and
`row.getSourceRowId()` when an action targets its source record. Totals count
each source record once, even when it belongs to several dimension values.

### Occurrence filtering [#occurrence-filtering]

Use `defineDataGridFilterDimension` when several filter fields must match the
same occurrence in a multi-value dimension.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="DataGridOccurrenceFilteringScenario" />
  </Tab>

  <Tab value="Code">
    ```tsx
    const membershipDimension = defineDataGridFilterDimension(
      membershipFilters,
    )<Membership>({
      columnId: "memberships",
      fields: {
        membershipRole: (membership) => membership.role,
        membershipStatus: (membership) => membership.status,
      },
    });

    const filters = useDataGridFilters({
      columns,
      definition: membershipFilters,
      dimensionFilters: [membershipDimension],
    });

    const table = useDataGridTable({
      ...filters.tableOptions,
      columns: filters.columns,
      data,
    });
    ```
  </Tab>
</Tabs>

Do not also put dimension filter fields in `fieldColumnMap`. With server-side
filtering, return only matching occurrences when the grouped result must match
the client-side view. See
[Filters](/components/filters#datagrid-tanstack-table-v9) for the full filtering
API.

## API Reference [#api-reference]

### DataGrid.PivotControl [#datagridpivotcontrol]

| Prop      | Type                              | Default | Description                                                                          |
| --------- | --------------------------------- | ------- | ------------------------------------------------------------------------------------ |
| `compact` | `boolean`                         | `true`  | Shows an icon-only trigger. Set to `false` to show the label and active field count. |
| `table`   | `DataGridTable<TData, TSelected>` | -       | Table whose Rows, Columns, and Values configuration the control edits. Required.     |

### Table options [#table-options]

| Option                                 | Type                                | Default       | Description                                                                                                              |
| -------------------------------------- | ----------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `enablePivoting`                       | `boolean`                           | `true`        | Enables pivot configuration for the table.                                                                               |
| `initialState.pivoting`                | `DataGridPivotingState`             | `null`        | Sets the initial Columns, Values, sorting, and totals configuration.                                                     |
| `initialState.pivoting.showSourceRows` | `boolean`                           | `true`        | Shows contributing records beneath summaries. Set to `false` to expand only between summary levels. Saved with the view. |
| `onPivotingChange`                     | `OnChangeFn<DataGridPivotingState>` | State updater | Receives changes when the consumer controls pivot state.                                                                 |

### Column options [#column-options]

| Option                 | Type                                                   | Default         | Description                                                          |
| ---------------------- | ------------------------------------------------------ | --------------- | -------------------------------------------------------------------- |
| `enablePivoting`       | `boolean`                                              | `true`          | Allows an accessor column to be used as a Rows or Columns dimension. |
| `pivotAggregationFns`  | `readonly DataGridPivotAggregation[]`                  | -               | Makes a measure available under Values with the listed aggregations. |
| `getDimensionValues`   | `(record, index) => readonly DataGridDimensionValue[]` | -               | Returns the occurrences for a multi-value dimension.                 |
| `filterDimensionValue` | `(dimensionValue, filterValue) => boolean`             | -               | Tests one occurrence against the column filter value.                |
| `sortDimensionValues`  | `(left, right) => number`                              | Label, then key | Sets the order of generated dimension values.                        |

### Pivot types [#pivot-types]

| Export                           | Type                                                                                                                                                             | Default              | Description                                                                      |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | -------------------------------------------------------------------------------- |
| `DataGridPivotingState`          | `{ columnDimensions, columnDimensionSortBy?, columnDimensionSorting?, rowDimensionSortBy?, values, showColumnTotals?, showRowTotals?, showSourceRows? } \| null` | `null`               | Complete serializable pivot configuration.                                       |
| `DataGridPivotValue`             | `{ columnId, aggregationFn, columnScope?, showAs? }`                                                                                                             | -                    | One active Value and how it is displayed. Overall Values do not accept `showAs`. |
| `DataGridPivotSortBy`            | `{ columnId, aggregationFn }`                                                                                                                                    | -                    | Active aggregate used to order a Rows or Columns dimension.                      |
| `DataGridPivotAggregation`       | `"count" \| "extent" \| "first" \| "last" \| "max" \| "mean" \| "median" \| "min" \| "sum" \| "unique" \| "uniqueCount"`                                         | -                    | Aggregation accepted by `pivotAggregationFns`.                                   |
| `DataGridPivotValueColumnScope`  | `"columnDimensions" \| "overall"`                                                                                                                                | `"columnDimensions"` | Whether a Value is split across Columns or displayed once overall.               |
| `DataGridPivotValueDisplay`      | `"value" \| "percentOfRowTotal" \| "percentOfColumnTotal" \| "percentOfGrandTotal"`                                                                              | `"value"`            | Calculation used to display a Value.                                             |
| `DataGridDimensionValue<TValue>` | `{ key, label, value }`                                                                                                                                          | -                    | One occurrence returned by a multi-value dimension.                              |

### Instance methods [#instance-methods]

| API                                | Signature                                                   | Description                                                                                         |
| ---------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `table.getIsPivoting`              | `() => boolean`                                             | Returns whether pivot Values are active.                                                            |
| `table.getPivotColumnDimensions`   | `() => string[]`                                            | Returns the current Columns field IDs.                                                              |
| `table.getPivotValues`             | `() => DataGridPivotValue[]`                                | Returns the active Values.                                                                          |
| `table.setPivoting`                | `(updater) => void`                                         | Updates the complete pivot configuration.                                                           |
| `table.setPivotColumnDimensions`   | `(updater) => void`                                         | Updates Columns without replacing Values.                                                           |
| `table.setPivotValues`             | `(updater) => void`                                         | Updates Values without replacing Columns.                                                           |
| `table.resetPivoting`              | `(defaultState?) => void`                                   | Restores the initial or empty pivot configuration.                                                  |
| `column.getCanPivotAsDimension`    | `() => boolean`                                             | Returns whether the column can be used in Rows or Columns.                                          |
| `column.getCanPivotAsValue`        | `() => boolean`                                             | Returns whether the column can be used under Values.                                                |
| `column.getIsPivotColumnDimension` | `() => boolean`                                             | Returns whether the column is active under Columns.                                                 |
| `column.getIsPivotValue`           | `() => boolean`                                             | Returns whether the column is an active Value.                                                      |
| `row.getDimensionValue`            | `(columnId) => DataGridDimensionValue<TValue> \| undefined` | Returns the typed occurrence for a multi-value dimension row.                                       |
| `row.getSourceRowId`               | `() => string \| undefined`                                 | Returns the source record ID for a leaf or occurrence row. Group and total rows return `undefined`. |
