You have flat sales rows: region, product, quarter, revenue. Stakeholders want a matrix—regions down the left, quarters across the top, summed revenue in every cell. That reshape is a React pivot table: row dimensions, column dimensions, and aggregated measures in a data grid.
In many libraries, pivot mode is locked behind an Enterprise license. AG Grid's interactive Pivot Panel is powerful—and priced accordingly. Simple Table ships declarative matrix pivot today so you can build analytics views in code or your own UI. A drag-and-drop Pivot Panel is on the Enterprise roadmap.
This React pivot table tutorial covers how pivot tables work in a JavaScript data grid, when to choose pivot vs row grouping, and how to configure Simple Table for aggregations, nested dimensions, totals, and runtime updates.
What Is a React Pivot Table?
A pivot table in React turns fact rows into a cross-tab matrix:
- • Rows — fields that stay on the left (e.g. region, product)
- • Columns — fields whose distinct values become dynamic headers (e.g. quarter, channel)
- • Values — measures aggregated into each cell (sum, average, count, min, max, or custom)
Flat input like { region: 'West', quarter: 'Q1', sales: 1200 } becomes a West row with a Q1 sales cell. Multiple source rows that share the same dimensions collapse into one aggregated cell—classic spreadsheet-style pivoting inside your React data grid.
Common use cases: revenue by region × quarter, units by product × channel, tickets by team × status, and any analytics dashboard where columns must be generated from data—not hardcoded in your header config.
Pivot Table vs Row Grouping in React
Use a matrix pivot table when…
- • You need dimensions as columns (quarters across the top)
- • Cell values are aggregates, not detail rows
- • Column headers should be generated from distinct field values
- • You want row/column/grand totals in the matrix
Use row grouping when…
- • You still need to see individual fact rows
- • Hierarchy is expand/collapse down the left, not a cross-tab
- • Columns stay fixed; you're organizing rows, not reshaping them
- • See the row grouping docs and tree data guide
While pivot is active in Simple Table, consumer rowGrouping is ignored. Multi-level pivot.rows builds its own expandable tree (e.g. region → product).
How to Build a React Pivot Table (Declarative Config)
Simple Table's matrix engine is declarative. Pass flat rows, a field catalog in columns, and a pivot config. Until the Enterprise Pivot Panel ships, your app owns the UI for choosing dimensions—presets, selects, analytics chrome, or TableAPI.setPivot.
That gives teams a practical AG Grid pivot alternative: matrix pivoting without Enterprise pricing today, with a first-party drag-and-drop panel coming for end-user field arrangement.
1import { SimpleTable } from "@simple-table/react";2import type { ReactColumnDef, Row } from "@simple-table/react";3import "@simple-table/react/styles.css";45const headers: ReactColumnDef[] = [6 { accessor: "region", label: "Region", width: 110, type: "string" },7 { accessor: "quarter", label: "Quarter", width: 80, type: "string" },8 { accessor: "sales", label: "Sales", width: 100, type: "number", align: "right" },9];1011const rows: Row[] = [12 { id: "r1", region: "West", quarter: "Q1", sales: 1200 },13 { id: "r2", region: "West", quarter: "Q2", sales: 1500 },14 { id: "r3", region: "East", quarter: "Q1", sales: 900 },15 { id: "r4", region: "East", quarter: "Q2", sales: 1100 },16];1718export default function RevenuePivot() {19 return (20 <SimpleTable21 columns={headers}22 rows={rows}23 // Same shape as the docs / analytics "Region × Quarter" preset24 pivot={{25 rows: ["region"],26 columns: ["quarter"],27 values: [{ accessor: "sales", aggregation: { type: "sum" } }],28 }}29 columnResizing30 height="400px"31 />32 );33}
When pivot is active, the grid does not show the field catalog as columns. It shows generated row-dimension columns plus dynamic value columns from the pivot result. Full prop reference lives in the pivot table docs.
Nested Pivot Dimensions, Multiple Measures, and Totals
Nested row fields
One row field renders a flat pivot. Multiple row fields build an expandable tree (docs/analytics "Region → Product" preset). Pass expandAll when you want nested levels open by default:
1<SimpleTable2 columns={headers}3 rows={rows}4 pivot={{5 rows: ["region", "product"],6 columns: ["quarter"],7 values: [{ accessor: "sales", aggregation: { type: "sum" } }],8 }}9 expandAll // demos set this when pivot.rows.length > 110 height="400px"11/>
Nested column groups
Multiple column fields produce nested headers (year → quarter):
1// Docs / analytics preset: "Category × Year → Quarter"2pivot={{3 rows: ["category"],4 columns: ["year", "quarter"],5 values: [{ accessor: "sales", aggregation: { type: "sum" } }],6}}
Multiple measures and totals
Add more value fields for side-by-side metrics—same pattern as the docs/analytics "Channel × Quarter" preset. Aggregation types match Simple Table's aggregate functions. Totals (showRowTotals, showColumnTotals, showGrandTotal) default to true:
1pivot={{2 rows: ["channel"],3 columns: ["quarter"],4 values: [5 { accessor: "sales", aggregation: { type: "sum" }, label: "Sales" },6 { accessor: "units", aggregation: { type: "sum" }, label: "Units" },7 ],8}}
Value-only layout (no column dimensions)
Pass an empty columns array to group and aggregate without generating a matrix of dynamic headers—same idea as the docs "Values only" preset:
1pivot={{2 rows: ["region", "category"],3 columns: [],4 values: [5 { accessor: "sales", aggregation: { type: "sum" } },6 { accessor: "cost", aggregation: { type: "sum" } },7 ],8}}
Update a React Pivot Table at Runtime (TableAPI)
The analytics demo and docs pivot demo swap presets by updating the controlled pivot prop (not setPivot). Nested row presets also pass expandAll:
1import { useState } from "react";2import { SimpleTable } from "@simple-table/react";3import type { PivotConfig } from "@simple-table/react";45const presets: { id: string; label: string; pivot: PivotConfig | null }[] = [6 { id: "source", label: "Source data", pivot: null },7 {8 id: "region-quarter",9 label: "Region × Quarter",10 pivot: {11 rows: ["region"],12 columns: ["quarter"],13 values: [{ accessor: "sales", aggregation: { type: "sum" } }],14 },15 },16 {17 id: "nested-rows",18 label: "Region → Product",19 pivot: {20 rows: ["region", "product"],21 columns: ["quarter"],22 values: [{ accessor: "sales", aggregation: { type: "sum" } }],23 },24 },25];2627export default function AnalyticsPivot() {28 const [activeId, setActiveId] = useState(presets[0].id);29 const active = presets.find((p) => p.id === activeId) ?? presets[0];30 const nestedRows = (active.pivot?.rows.length ?? 0) > 1;3132 return (33 <>34 {presets.map((preset) => (35 <button key={preset.id} type="button" onClick={() => setActiveId(preset.id)}>36 {preset.label}37 </button>38 ))}39 <SimpleTable40 columns={headers}41 rows={rows}42 pivot={active.pivot}43 expandAll={nestedRows}44 height="480px"45 />46 </>47 );48}
Prefer imperative control? TableAPI.setPivot (documented on the pivot docs) updates the same config without remounting:
1const tableRef = useRef<TableAPI>(null);23tableRef.current?.setPivot({4 rows: ["region"],5 columns: ["quarter"],6 values: [{ accessor: "sales", aggregation: { type: "sum" } }],7});8tableRef.current?.setPivot(null); // back to source rows
- •
setPivot(config | null)— enable, update, or clear pivot - •
getPivot()— current config - •
getPivotHeaders()/getPivotedRows()— inspect the generated matrix
React Pivot Table Behavior Notes
- Filters run on source rows before pivoting. Row-dimension columns keep their source accessors, so filtering by region still works. Generated measure columns are not filterable.
- Sort and quick filter apply to the pivoted view.
- CSV export exports the pivoted matrix, not the original fact table.
- Pivoted measure cells are aggregated summaries—not meant for cell editing.
- High column cardinality (many distinct column-field values) creates many leaf columns. Watch horizontal scroll and performance.
What's next: An interactive drag-and-drop Pivot Panel is coming for Simple Table Enterprise—end users will arrange row, column, and value fields without custom UI. Declarative pivot stays the foundation; the panel will drive the same config.
React Pivot Table FAQ
What is a React pivot table?
A React pivot table reshapes flat rows into a matrix: row dimensions stay on the left, column dimensions become dynamic headers, and value fields are aggregated (sum, average, count, min, max, or custom) into each cell.
Is there a free alternative to AG Grid pivot mode?
Yes. Simple Table provides declarative matrix pivot without AG Grid Enterprise. You configure rows, columns, and values via props or TableAPI. An interactive drag-and-drop Pivot Panel is on the Simple Table Enterprise roadmap.
When should I use pivot vs row grouping in a React data grid?
Use matrix pivot when you need cross-tab columns generated from data and aggregated cells. Use row grouping when you still need individual fact rows in an expand/collapse hierarchy with fixed columns.
Does Simple Table include a drag-and-drop Pivot Panel?
Declarative pivot is available today. A first-party drag-and-drop Pivot Panel is coming for Simple Table Enterprise and will drive the same pivot config API.
Can I update a React pivot table at runtime?
Yes. The analytics and docs demos swap a controlled pivot prop (and expandAll for nested row fields). You can also call TableAPI.setPivot to enable, change, or clear pivot imperatively. getPivot, getPivotHeaders, and getPivotedRows inspect the active matrix.
Ship a React Pivot Table Without Waiting on Enterprise
You can ship a React pivot table today without AG Grid Enterprise or hand-rolled matrix math. With a declarative pivot config you get:
- Row / column / value dimensions with sum, average, count, and more
- Nested dimensions and row/column/grand totals
- Runtime updates via TableAPI for analytics presets
- A clear path to an Enterprise Pivot Panel on the same config model