Documentation

JavaScript

The PivotGrid does not need React. @kanunilabs/pivotgrid is a second renderer over the same engine (@kanunilabs/pivotgrid-core) and the same stylesheet — aggregation in the Web Worker, drag-and-drop between areas, filters, sorting, expand/collapse, saved layouts and theming behave identically, and a parity ledger checked in CI records, surface by surface, where the two renderers agree and where they deliberately do not.

npm i @kanunilabs/pivotgrid

A pivot in one call

import { createPivotGrid, type PivotField } from '@kanunilabs/pivotgrid';
import '@kanunilabs/pivotgrid/styles.css';

const data = [
  { region: 'EMEA', quarter: 'Q1', revenue: 1200 },
  { region: 'EMEA', quarter: 'Q2', revenue: 980 },
  { region: 'APAC', quarter: 'Q1', revenue: 1500 },
];

const fields: PivotField<(typeof data)[number]>[] = [
  { id: 'region', dataField: 'region', caption: 'Region', dataType: 'string', area: 'row' },
  { id: 'quarter', dataField: 'quarter', caption: 'Quarter', dataType: 'string', area: 'column' },
  { id: 'revenue', dataField: 'revenue', caption: 'Revenue', dataType: 'number', area: 'data', summaryType: 'sum' },
];

const grid = createPivotGrid(document.getElementById('host'), {
  gridId: 'sales',
  data,
  initialFields: fields,
  locale: 'tr',            // one of 12; 'ar' switches to RTL
  theme: 'quartz',
});

grid.setData(nextRows);          // live data
grid.setLocale('de');            // relabels chrome, chart axes and dialogs
grid.controller.expandAll();     // the engine, directly
grid.destroy();                  // when the host goes away

The same data and fields as the React quick start — the field vocabulary (area, dataType, summaryType, format, groupInterval) is core's, not the renderer's, so it is identical in both.

The config mirrors the React props. Everything BasePivotGrid takes as a prop, createPivotGrid takes as a key: layout, stateStoring, toolbar, striped, dictionary, rtl, workerUrl, keyboardNavigation, disabled, and the event callbacks (onCellClick, onFieldsChange, onLayoutChange, onExporting, onError, …). One difference in the shape of one payload: onCellClick receives the native MouseEvent, where React hands you a synthetic one.

The overlap is not total, and the renderer tells you where it ends rather than letting you guess: pass a React-only option and it says so in the console, naming what to write instead. The React-only keys are the three render slots (renderCalculatedFieldModal, renderPrefilterBuilder, renderNodeContextMenu), onOpenFullScreenChart (deprecated in React too), children and ref. Each has a plain-callback or Enterprise-package counterpart, and the warning names it.

grid.controller is the engine itself — the same PivotController the React binding exposes. Everything imperative (fields, layout, filters, expansion, export, the cell-click resolver) lives there; see the API reference. The JavaScript surface itself — createPivotGrid and PivotGridVanillaConfig — has its own generated pages.

Three differences worth knowing

  • Row headers are sticky, not a second table. One scroll container; the row-header cells pin themselves with computed insets, and their widths are budgeted against the viewport so the data never disappears behind them. A dragged row-header width is stored as the configured width, so a saved layout means the same thing at any window size.
  • Sockets, not render props. Where React takes renderCalculatedFieldModal, vanilla exposes onAddCalculatedField / onEditCalculatedField, onOpenPrefilter and onToggleChart. When a callback is present the footer draws the matching button; absent, nothing is drawn. The Enterprise package fills all four — an app may fill any.
  • Charts are a subscription. createChartSync(controller, getLocale, options, onSync) is the React hook usePivotChartSync as a plain subscription: 150 ms after the result, the expansion set or the locale changes, your transformer runs. The Community bridge ships no transformer; the Enterprise package does.

Script tag (UMD)

No bundler? The package ships a self-contained build:

<link rel="stylesheet" href="https://unpkg.com/@kanunilabs/pivotgrid/dist/styles.css" />
<script src="https://unpkg.com/@kanunilabs/pivotgrid/dist/pivotgrid.umd.js"></script>
<script>
  const { createPivotGrid } = KanuniLabsPivotGrid;
  createPivotGrid(document.getElementById('host'), { /* the same config */ });
</script>

The engine and its Web Worker are bundled in — the worker starts from a Blob URL, so there is no file to serve. If a strict Content-Security-Policy blocks blob: workers, pass workerUrl as described under Installation.

Enterprise edition

@kanunilabs/pivotgrid-enterprise wraps the Community renderer — it does not fork it — and fills the sockets it opens: the no-code calculated-field designer, the visual prefilter builder, the drill-down modal, an eight-type chart panel and styled Excel & PDF export. It is installed from the private registry with your licence.

import { createEnterprisePivotGrid } from '@kanunilabs/pivotgrid-enterprise';
import '@kanunilabs/pivotgrid/styles.css';   // no separate Enterprise stylesheet

const grid = createEnterprisePivotGrid(host, {
  licenseKey: 'KLAB1.…',
  // everything createPivotGrid takes, plus — all default ON:
  calculatedFields: true,   // footer calculator → the designer
  prefilter: true,          // footer filter → the AND/OR builder
  drillDown: true,          // click a value cell → the source rows
  charts: true,             // footer chart button → the panel, hidden until pressed
  chartType: 'column',
  drillDownPageSize: 50,
  data,
  initialFields: fields,
});

grid.openCalculatedFieldDesigner();          // or (fieldId) to edit one
grid.openPrefilterBuilder();
grid.openDrillDown({ records, rowPath: ['EMEA'] });
grid.toggleChart();
grid.setChartType('pie');
grid.isEnterpriseLicensed();

Without a valid key the grid still works, watermarked — your app never breaks on a licensing problem. One licence key opens both Enterprise packages, React and plain JavaScript alike; see Enterprise & licensing.

drillDown chains your own onCellClick rather than replacing it: your handler runs first and sees every click, whether or not a modal follows. Pass drillDown: false to keep the click for yourself and call openDrillDown when you want the dialog.

Charts without a charting library

The chart panel draws its eight types — column, bar, line, area, pie, scatter, radar and combination — as SVG the package writes itself. There is no charting dependency, for a reason that decided itself: a library marked external survives into the script-tag bundle as a bare import('…') that a classic script cannot resolve, and a second <script> tag defines a global that a dynamic import never looks at. Bundling a library would have added it to every customer's page; leaving it out would have made charts unreachable from the one delivery path this package exists for.

The panel also carries what the React chart never had: axis captions in the grid's locale (so the axis agrees with the cells above it), a note whenever maxPoints or maxSeries dropped data, and a visually-hidden data table so a screen reader gets the numbers, not just "graphic".

Excel and PDF from a script tag

The export peers are optional and never bundled. A bundler resolves them the usual way; a script-tag page provides them as globals, which is what their own UMD builds define:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/exceljs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jspdf.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jspdf.plugin.autotable.js"></script>
<script src="https://unpkg.com/@kanunilabs/pivotgrid-enterprise/dist/pivotgrid-enterprise.umd.js"></script>

The package looks for globalThis.ExcelJS and globalThis.jspdf first and falls back to import() — a page that loads the tags in any order works, and a click that lands before a tag has loaded is not remembered as a failure. If a peer is nowhere, the export does nothing and the console says which tag to add. A missing peer never throws into your click handler.

The Enterprise UMD bundles the Community renderer and the licensing core (global KanuniLabsPivotGridEnterprise), so one stylesheet and one script tag are enough.

Where it stops

The Enterprise parity ledger grades every paid surface against the React edition and is exported from the package, so the limits below are checked, not remembered:

  • PDF has no embedded Unicode font (shared with React). Labels the built-in font cannot draw fall back to English rather than rendering as glyph soup; cell text outside Latin-1 is a known limit.
  • Very large pivots export as SpreadsheetML (.xls), above 150,000 engine cells, and that path carries no conditional formatting, freeze pane or outline levels (shared with React). Unlike React, the console says so when the switch happens.
  • Chart series colours ignore the theme (shared with React); chrome — axes, gridlines, labels — follows it.
  • A prefilter on a date field with a groupInterval compares the group key, not the date, in both editions. The builder therefore offers such a field a text box and no ordering operators, so what you type is what is compared.

What is the same, what is different

ReactJavaScript
Engine, stylesheet, themes, workersharedshared
Config / props<BasePivotGrid {...props} />createPivotGrid(host, props)
Enterprise surfacesrender propsplain callbacks (sockets)
Cell click payloadsynthetic eventnative MouseEvent
Imperative APIref → controllergrid.controller
ChartsusePivotChartSync + rechartscreateChartSync + built-in SVG panel
Server-side renderingrenders markupconstructs on mount (needs a document)
Enterprise<EnterprisePivotGrid>createEnterprisePivotGrid()

SSR is the one honest difference: the JavaScript renderer produces DOM, so it is created when the host element exists. Everything else is held to parity by the ledgers and conformance suites that run on every commit.