ProPivot is an enterprise-grade, open-source JavaScript library for building pivot tables
over millions of rows. It works with React, Angular, Vue, and any
JavaScript framework — or plain JavaScript via a <script> tag. Lightning fast,
lightweight, feature-rich, highly configurable — and instantly embedded in any project. The columnar
engine aggregates entirely in the browser, with no server round-trips.
Live demo
This grid is the actual library running in your browser on a generated dataset — no server, no iframe. Change the controls, drag fields, double-click a cell to drill through, export.
Tip: hover a column header and click ▾ to open its properties (aggregation, conditional formatting, display format, filter); drag a header edge to resize, or drag headers to reorder; double-click a value cell to drill through.
Features
A framework-agnostic core behind a stable facade — the same engine powers the headless API, the Web Worker, and the virtualized grid.
Sum, average, median, distinct-count, stdev, plus the positional family — difference, %difference, runningtotals — along a configurable row/column axis.
Formula parser + evaluator: sum('sales')/sum('qty'). Mix calculated and native measures freely; each measure aggregates independently.
Both #value# and #value dialects. Color cells by threshold; bring your own classes via customizeCell.
Viewport-only DOM with frozen headers handles high-cardinality data smoothly. Compact, flat, and classic layouts.
Move fields between Rows / Columns / Measures / Filters, change a measure's aggregation, sort by measure, Top-N filter — all at runtime.
CSV, HTML, real .xlsx, dependency-free .pdf, and an .svg image of the grid (rasterizes to PNG in-browser).
Offload aggregation off the main thread behind one async interface — falls back automatically when workers aren't available.
Opt-in: offload aggregation of millions of rows to in-browser DuckDB SQL GROUPING SETS, with automatic, result-identical fallback to the built-in engine.
First-class <Pivot> components for React, Vue, and an <pro-pivot> Angular wrapper. Or read window.ProPivot from a plain <script> — Svelte, jQuery, a WebView, anything.
A two-layer golden suite pins both the computed cell matrix and the rendered grid, so the compatibility contract can't silently drift.
Works with any JavaScript framework
First-class React, Vue, and Angular components — or read window.ProPivot from a plain <script> for Svelte, jQuery, server-rendered pages, or an Android WebView. One report object drives them all. Full docs →
React
import { Pivot } from '@proteus/propivot/react'; import '@proteus/propivot/propivot.css'; <Pivot report={report} toolbar onReady={(pivot) => console.log('ready', pivot)} />;
Vue
import { Pivot } from '@proteus/propivot/vue'; import '@proteus/propivot/propivot.css'; <Pivot :report="report" toolbar @ready="onReady" />
Angular
// import { ProPivotComponent } from './pro-pivot.component'; <pro-pivot [report]="report" [toolbar]="true" (cellclick)="onCellClick($event)"> </pro-pivot>
Plain JavaScript
<!-- defines window.ProPivot --> <script src="propivot.global.js"></script> new ProPivot({ container: '#pivot', toolbar: true, report });
Usage
Describe the slice, measures, formats, and conditions declaratively. ProPivot does the rest.
const pivot = new ProPivot({ container: '#pivot', toolbar: true, report: { dataSource: { type: 'json', data, mapping }, slice: { rows: [{ uniqueName: 'region' }, { uniqueName: 'category' }], columns: [{ uniqueName: 'year' }], measures: [ { uniqueName: 'sales', aggregation: 'sum', format: 'cur' }, { uniqueName: 'aov', formula: "sum('sales')/sum('qty')", caption: 'Avg Price' }, { uniqueName: 'sales', aggregation: 'runningtotals', positionalAxis: 'rows' }, ], }, formats: [{ name: 'cur', currencySymbol: '$', decimalPlaces: 0 }], conditions: [{ formula: '#value > 100000', measure: 'sales', format: { backgroundColor: '#c5e1a5' } }], }, reportcomplete: () => console.log('ready'), });