Interactive Pivot Tables in Vanilla JS and React – Pvotly

Category: Javascript , Table | June 21, 2026
Authorartainjast
Last UpdateJune 21, 2026
LicenseMIT
Views0 views
Interactive Pivot Tables in Vanilla JS and React – Pvotly

Pvotly is a framework-agnostic pivot table library that transforms flat records into cross-tab reports with drag-and-drop field arrangement, multiple aggregations, filtering, sorting, and export.

You can use it as a React component, a vanilla JavaScript widget, or as a pure headless engine that computes the grid for a custom renderer.

Features:

  • Generates cross-tab reports from object records, matrices, CSV text, and async data sources.
  • Renders a drag-and-drop field list for Rows, Columns, Values, and Report Filters.
  • Aggregates values with sum, count, distinct count, average, median, minimum, maximum, product, first, last, variance, and standard deviation.
  • Calculates custom measures from arithmetic formulas and aggregate references.
  • Sorts field members or entire axes by aggregated measure values.
  • Filters members by selection, labels, values, numeric ranges, and date ranges.
  • Groups date fields into year, quarter, month, week, day, and time hierarchies.
  • Displays grand totals and subtotals in compact, classic, or flat layouts.
  • Styles matching cells through measure-specific conditional formatting rules.
  • Exports the current grid as CSV, Excel-compatible markup, HTML, JSON, or a print view.
  • Switches between light and dark themes and accepts runtime design-token overrides.
  • Exposes a headless grid model for custom DOM, canvas, server, or framework renderers.

Use Cases:

  • Business intelligence dashboards that need to summarize sales, inventory, or marketing data with drag-and-drop pivot tables inside a React admin panel.
  • Customer analytics platforms where support agents filter support ticket volumes by region, priority, and resolution time using a headless engine with a custom chart renderer.
  • Embedded reporting in a SaaS product that lets users upload CSV files, arrange dimensions, and export formatted cross-tabs to Excel.
  • Logistics monitoring tools that group shipment delays by carrier and month, using date parts to drill from year down to day without extra data preprocessing.
  • Financial planning interfaces where analysts apply conditional formatting to highlight budget vs. actual variances and share the view via the print/PDF export.

How To Use It:

Installation

Choose the package that matches the rendering layer in your project. @pvotly/web and @pvotly/react re-export the core engine and its shared types.

# Vanilla JavaScript or TypeScript with the bundled UI.
npm install @pvotly/web
# React application with the bundled UI.
npm install @pvotly/react @pvotly/web
# Headless pivot engine only.
npm install @pvotly/core

Import the shared stylesheet once when you use the DOM widget or React component.

import '@pvotly/web/styles.css';

Basic Usage

Get started with a small set of object records and one clear row, column, and measure mapping. The DOM widget mounts into a selector or an existing HTMLElement.

<div id="revenue-report"></div>
import { PivotTable } from '@pvotly/web';
import '@pvotly/web/styles.css';
const revenueRecords = [
  { region: 'West', product: 'Laptop', sales: 18400, orders: 36 },
  { region: 'West', product: 'Monitor', sales: 7200, orders: 48 },
  { region: 'East', product: 'Laptop', sales: 15600, orders: 31 },
  { region: 'East', product: 'Monitor', sales: 6900, orders: 46 },
];
const report = new PivotTable('#revenue-report', {
  height: 460,
  dataSource: { data: revenueRecords },
  slice: {
    rows: [{ uniqueName: 'region' }],
    columns: [{ uniqueName: 'product' }],
    measures: [
      { uniqueName: 'sales', aggregation: 'sum', caption: 'Sales' },
      { uniqueName: 'orders', aggregation: 'sum', caption: 'Orders' },
    ],
  },
  options: {
    grid: {
      type: 'compact',
      showGrandTotals: 'on',
    },
  },
});
report.on('cellClick', ({ cell }) => {
  console.log(cell.formatted);
});

The widget displays its toolbar and field-list panel by default. Drag a field between Rows, Columns, Values, and Report Filters to update the report definition.

Advanced Usages

Load pvotly from a CDN

<link
  rel="stylesheet"
  href="https://unpkg.com/@pvotly/[email protected]/dist/pvotly.global.css"
/>
<div id="inventory-report" style="height: 440px"></div>
<script src="https://unpkg.com/@pvotly/[email protected]/dist/pvotly.global.js"></script>
<script>
  const inventory = [
    { warehouse: 'Dallas', category: 'Hardware', quantity: 62 },
    { warehouse: 'Dallas', category: 'Accessories', quantity: 119 },
    { warehouse: 'Seattle', category: 'Hardware', quantity: 44 },
  ];
  new Pvotly.PivotTable('#inventory-report', {
    dataSource: { data: inventory },
    slice: {
      rows: [{ uniqueName: 'warehouse' }],
      columns: [{ uniqueName: 'category' }],
      measures: [{ uniqueName: 'quantity', aggregation: 'sum' }],
    },
  });
</script>

Render a React Pivot Table

import { useRef } from 'react';
import { PivotTable, type PivotTableHandle } from '@pvotly/react';
import '@pvotly/web/styles.css';
const supportTickets = [
  { team: 'Billing', status: 'Open', tickets: 18 },
  { team: 'Billing', status: 'Closed', tickets: 62 },
  { team: 'Technical', status: 'Open', tickets: 27 },
  { team: 'Technical', status: 'Closed', tickets: 91 },
];
export default function TicketReport() {
  const tableRef = useRef<PivotTableHandle>(null);
  return (
    <>
      <button onClick={() => tableRef.current?.exportTo('csv')}>
        Export report
      </button>
      <PivotTable
        ref={tableRef}
        height={480}
        dataSource={{ data: supportTickets }}
        slice={{
          rows: [{ uniqueName: 'team' }],
          columns: [{ uniqueName: 'status' }],
          measures: [{ uniqueName: 'tickets', aggregation: 'sum' }],
        }}
        onCellDoubleClick={({ records }) => {
          console.log(records);
        }}
      />
    </>
  );
}

Parse CSV Data and Group Dates

import { PivotTable } from '@pvotly/web';
import '@pvotly/web/styles.css';
const csvText = `orderedAt,market,revenue
2026-01-12,North,4200
2026-02-07,South,5100
2026-04-18,North,3900`;
new PivotTable('#order-report', {
  dataSource: {
    csv: csvText,
    csvOptions: {
      header: true,
      dynamicTyping: true,
    },
    mapping: {
      orderedAt: {
        type: 'date',
        dateParts: ['year', 'quarter'],
      },
      revenue: {
        aggregation: 'sum',
      },
    },
  },
  slice: {
    rows: [
      { uniqueName: 'orderedAt.year' },
      { uniqueName: 'orderedAt.quarter' },
    ],
    columns: [{ uniqueName: 'market' }],
    measures: [{ uniqueName: 'revenue', aggregation: 'sum' }],
  },
});

Add a Calculated Measure

const financeReport = {
  dataSource: {
    data: [
      { department: 'Online', revenue: 14800, cost: 6900 },
      { department: 'Retail', revenue: 12100, cost: 5800 },
    ],
  },
  slice: {
    rows: [{ uniqueName: 'department' }],
    measures: [
      { uniqueName: 'revenue', aggregation: 'sum', caption: 'Revenue' },
      { uniqueName: 'cost', aggregation: 'sum', caption: 'Cost' },
      {
        uniqueName: 'grossMargin',
        caption: 'Gross Margin',
        formula: "sum('revenue') - sum('cost')",
      },
    ],
  },
};

Supported aggregate functions in formulas include sum, count, distinctCount, average, median, min, max, product, first, last, stdev, stdevp, var, and varp. A formula with invalid syntax throws a SyntaxError.

Apply Number Formats and Conditional Formatting

new PivotTable('#kpi-report', {
  dataSource: {
    data: [
      { department: 'Support', completionRate: 0.92 },
      { department: 'Sales', completionRate: 0.78 },
    ],
  },
  slice: {
    rows: [{ uniqueName: 'department' }],
    measures: [
      {
        uniqueName: 'completionRate',
        aggregation: 'average',
        format: 'percent',
      },
    ],
  },
  formats: [
    {
      name: 'percent',
      style: 'percent',
      minimumFractionDigits: 1,
      maximumFractionDigits: 1,
    },
  ],
  conditions: [
    {
      measure: 'completionRate',
      condition: { op: '<', value: 0.8 },
      format: { color: '#b42318', fontWeight: '700' },
    },
  ],
});

Build a Custom Headless Renderer

import { PivotEngine } from '@pvotly/core';
const engine = new PivotEngine({
  dataSource: {
    data: [
      { plan: 'Basic', month: 'January', subscribers: 180 },
      { plan: 'Pro', month: 'January', subscribers: 92 },
      { plan: 'Basic', month: 'February', subscribers: 203 },
    ],
  },
  slice: {
    rows: [{ uniqueName: 'plan' }],
    columns: [{ uniqueName: 'month' }],
    measures: [{ uniqueName: 'subscribers', aggregation: 'sum' }],
  },
});
const grid = engine.getGrid();
for (const row of grid.rowLeaves) {
  for (const column of grid.columnLeaves) {
    const measure = grid.measures[0];
    const cell = grid.getCell(row, column, measure);
    console.log(row.caption, column.caption, cell.formatted);
  }
}

Configuration Options

Root Report Configuration

  • dataSource (DataSourceConfig): Defines the records that feed the report. Provide data, matrix, or csv for a local source. Add remote or fetcher for an async source.
  • slice (Slice): Defines the rows, columns, measures, filters, sorting, and expanded members.
  • options (PivotOptions): Defines grid layout, configurator behavior, formatting defaults, drill-through behavior, and rendering controls.
  • formats (NumberFormat[]): Defines named formats that measures reference through measure.format.
  • conditions (ConditionalFormat[]): Defines ordered conditional formatting rules.
  • localization (Localization): Overrides report labels such as grand totals, blank members, and aggregation captions.
  • valuesAxis ('columns' | 'rows'): Places measures on the columns axis or the rows axis. This setting takes precedence over options.grid.measurePosition.
  • customAggregators (Record<string, AggregatorDefinition>): Registers report-level aggregation names beside the built-in aggregations.
  • locale (string): Sets a grid-wide BCP-47 locale fallback for Intl number and date formatting.
  • serverSide (ServerSideConfig): Supplies an async query function that returns a ready-to-render PivotGrid for server-side aggregation.

Data Source Options

  • data (DataRecord[]): Accepts an array of flat objects.
  • matrix (DataValue[][]): Accepts an array of arrays. The first row supplies field names.
  • csv (string): Accepts raw CSV text.
  • csvOptions.delimiter (string): Sets the CSV separator.
  • csvOptions.header (boolean): Treats the first CSV row as field names. The default is true.
  • csvOptions.quote (string): Sets the CSV quote character. The default is ".
  • csvOptions.trim (boolean): Trims whitespace around parsed values. The default is true.
  • csvOptions.dynamicTyping (boolean): Coerces numeric, boolean, and date values during parsing. The default is true.
  • remote (RemoteDataSource): Describes a remote json or csv endpoint. Set url, then add fetchOptions, transform, or refreshInterval when required.
  • fetcher (() => Promise<DataRecord[]> | DataRecord[]): Supplies records through a custom async or synchronous function.
  • mapping (FieldMap): Overrides source-field metadata.

Field Mapping Options

  • caption (string): Replaces the field label in the UI.
  • type ('string' | 'number' | 'boolean' | 'date' | 'datetime'): Overrides automatic field-type detection.
  • dateParts (DatePart[]): Expands a date field into year, quarter, month, monthName, week, dayOfMonth, weekday, date, hour, minute, or second.
  • visible (boolean): Hides the field from the available-field list.
  • isMeasure (boolean): Marks a numeric field as measure-only.
  • aggregation (AggregationName): Sets the default aggregation for a field in Values.
  • format (string): Sets the default named number format.

Slice Options

  • rows (SliceField[]): Places dimensions on the row axis.
  • columns (SliceField[]): Places dimensions on the column axis.
  • measures (MeasureConfig[]): Defines Values-axis aggregations and formulas.
  • reportFilters (SliceField[]): Filters the whole report and keeps the field off the grid axes.
  • expands (ExpandsConfig): Stores expanded header paths through expandAll, rows, and columns.
  • drills (DrillsConfig): Stores collapsed header paths through drillAll, rows, and columns.
  • sorting (SortingConfig): Sorts the row or column axis from a measure and optional opposite-axis tuple.
  • flatSort (Array<{ uniqueName: string; sort: SortDirection }>): Controls sort order in the flat grid layout.

Slice Field Options

  • uniqueName (string): Names a source field or date part such as orderedAt.quarter.
  • caption (string): Replaces the field caption for this report placement.
  • sort ('asc' | 'desc' | 'unsorted'): Sorts members on the current axis.
  • filter (FieldFilter): Attaches a member, value, label, or query filter.

Measure Options

  • uniqueName (string): Names a source field or a unique calculated-measure identifier.
  • caption (string): Replaces the measure caption.
  • aggregation (AggregationName): Uses sum, count, distinctCount, average, median, min, max, product, first, last, stdev, stdevp, var, varp, none, or a custom aggregation name.
  • showDataAs (ShowDataAs): Applies raw, percentage, parent percentage, running total, rank, or difference-from-previous transformations.
  • format (string): References a named format.
  • formula (string): Defines a calculated measure. A formula overrides aggregation.
  • grandTotalCaption (string): Replaces the measure caption for its grand total.
  • active (boolean): Controls whether the measure participates in the report.

Filter Options

  • members (MemberFilter): Uses include or exclude arrays for explicit member selection.
  • value (ValueFilter): Uses a measure and top, bottom, equal, notEqual, greater, greaterEqual, less, lessEqual, between, or notBetween rules.
  • label (LabelFilter): Uses contains, notContains, beginsWith, endsWith, equal, notEqual, or regex rules against member captions.
  • query (QueryFilter): Uses field predicates such as min, max, greater, less, equal, after, and before.

Grid and Engine Options

  • grid.type ('compact' | 'classic' | 'flat'): Selects the layout used for nested headers and subtotal lines.
  • grid.showGrandTotals ('on' | 'off' | 'rows' | 'columns'): Shows grand totals on both axes or one axis.
  • grid.showTotals ('on' | 'off' | 'rows' | 'columns'): Shows subtotals for nested groups.
  • grid.showHeaders (boolean): Shows row and column header titles.
  • grid.showFilter (boolean): Shows field-list filter icons in grid headers.
  • grid.title (string): Sets the empty top-left grid caption.
  • grid.rowLayout ('compact' | 'gutter'): Uses an indented row-label column or an outline-style expander gutter.
  • grid.rowLabelsCaption (string): Sets the row-labels caption in gutter layout.
  • grid.measurePosition ('columns' | 'rows'): Places measures on columns or rows when valuesAxis is not set.
  • grid.width ('fill' | 'content'): Stretches the grid to its container or preserves content width.
  • configuratorActive (boolean): Opens or hides the field-list configurator.
  • configuratorButton (boolean): Shows or hides the configurator toolbar button.
  • showAggregationLabels (boolean): Displays aggregation labels with measures.
  • defaultAggregation (AggregationType): Selects the reducer assigned to newly added Values fields.
  • virtualization (boolean): Enables virtualized rendering.
  • drillThrough (boolean): Enables source-record access from a body cell.
  • readOnly (boolean): Hides configuration controls.
  • datePattern (string): Sets the default date display pattern.
  • dateTimePattern (string): Sets the default date-time display pattern.
  • locale (string): Sets the engine-level Intl locale.
  • useWorker (boolean): Requests Web Worker grid computation for async grid building.
  • worker (WorkerConfig): Supplies a worker url or factory for useWorker.

DOM Widget Options

  • toolbar (boolean): Shows the toolbar. The default is true.
  • fieldList (boolean): Shows the drag-and-drop field-list panel. The default is true.
  • theme ('light' | 'dark' | string): Selects a built-in theme or a custom theme name.
  • height (string | number): Sets widget height.
  • width (string | number): Sets widget width.

Number Format Options

  • name (string): Names the format. Measures reference this value through format.
  • decimalPlaces (number): Sets fixed decimal precision.
  • maxDecimalPlaces (number): Sets the maximum fractional precision.
  • decimalSeparator (string): Replaces the decimal separator.
  • thousandsSeparator (string): Replaces the grouping separator. Use an empty string to suppress grouping.
  • currencySymbol (string): Adds a currency or unit prefix or suffix.
  • currencySymbolAlign ('left' | 'right'): Sets currency-symbol position.
  • isPercent (boolean): Multiplies the value by 100 and appends %.
  • negativeFormat ('minus' | 'parentheses' | 'redMinus'): Formats negative values.
  • nullValue (string): Sets text for blank cells.
  • infinityValue (string): Sets text for invalid numeric results.
  • dateTimePattern (string): Sets a date pattern with yyyy, yy, MM, dd, HH, mm, and ss.
  • textAlign ('left' | 'center' | 'right'): Aligns formatted cells.
  • intl (boolean): Forces the Intl formatter path.
  • locale (string): Overrides the report-level Intl locale.
  • style ('decimal' | 'currency' | 'percent'): Sets the Intl number style.
  • currency (string): Supplies an ISO 4217 code for Intl currency formatting.
  • currencyDisplay ('symbol' | 'narrowSymbol' | 'code' | 'name'): Controls the Intl currency label.
  • minimumFractionDigits (number): Sets the Intl minimum fractional precision.
  • maximumFractionDigits (number): Sets the Intl maximum fractional precision.
  • useGrouping (boolean): Toggles Intl grouping separators.
  • dateTimeFormat (Intl.DateTimeFormatOptions): Supplies Intl date and time options.

Conditional Formatting and Localization Options

  • conditions[].measure (string): Restricts a rule to one measure.
  • conditions[].condition (ConditionExpr): Tests a cell with =, !=, >, >=, <, <=, between, contains, isTrue, or isFalse.
  • conditions[].format (CellStyle): Sets backgroundColor, color, fontFamily, fontSize, fontWeight, fontStyle, or textAlign.
  • localization.grandTotal (string): Replaces the grand-total label.
  • localization.grandTotalLabel (string): Supplies an alternate grand-total label.
  • localization.totalLabel (string): Sets the subtotal template.
  • localization.blankMember (string): Replaces blank member labels.
  • localization.noData (string): Replaces the empty-grid message.
  • localization.aggregations (Partial<Record<AggregationType, string>>): Replaces aggregation captions.

API Methods

PivotEngine Methods

import { PivotEngine } from '@pvotly/core';
const engine = new PivotEngine(reportConfig);
// Replace the full data source.
engine.updateData({ data: nextRecords });
// Read the normalized data model and field metadata.
engine.getDataset();
engine.getFields();
engine.getMembers('region');
// Read or replace report configuration.
engine.getConfiguration();
engine.getReport();
engine.setConfiguration(nextReportConfig);
engine.setReport(nextReportConfig);
engine.getSlice();
engine.setSlice(nextSlice);
engine.setOptions({ grid: { type: 'classic' } });
engine.setFormats(nextFormats);
engine.setConditions(nextConditions);
// Move or remove fields across report axes.
engine.setFieldAxis('region', 'rows', 0);
engine.addToRows('region');
engine.addToColumns('product');
engine.addToValues('sales');
engine.addToFilters('year');
engine.removeField('year');
// Replace measures or edit their calculation settings.
engine.setMeasures(nextMeasures);
engine.addCalculatedMeasure({
  uniqueName: 'profit',
  formula: "sum('revenue') - sum('cost')",
});
engine.setAggregation('sales', 'average');
engine.setMeasureFormat('sales', 'currency');
engine.setShowDataAs('sales', 'percentOfGrandTotal');
// Sort members or an axis by measure values.
engine.sortField('region', 'desc');
engine.sortByValue('rows', {
  direction: 'desc',
  measure: 'sales',
});
// Apply or clear a field filter.
engine.setFilter('region', {
  type: 'members',
  include: ['West', 'East'],
});
engine.clearFilter('region');
// Expand or collapse report members.
engine.expand('rows', {
  tuple: [{ uniqueName: 'region', value: 'West' }],
});
engine.collapse('rows', {
  tuple: [{ uniqueName: 'region', value: 'West' }],
});
engine.expandAll();
engine.collapseAll();
// Read source records or the computed pivot grid.
engine.getRecords();
engine.getGrid();

Dataset and Core Utility Methods

import {
  Dataset,
  buildGrid,
  compareValues,
  createAggregator,
  formatValue,
  formatDate,
  resolveFormat,
  compileFormula,
  resolveCellStyle,
  datePartValue,
  datePartCaption,
  toDate,
  parseCsv,
  inferFieldType,
  normalizeValue,
} from '@pvotly/core';
const dataset = new Dataset({ data: revenueRecords });
// Inspect normalized records, fields, values, and captions.
dataset.parseName('orderedAt.quarter');
dataset.fieldType('sales');
dataset.resolveValue(revenueRecords[0], 'sales');
dataset.memberCaption('region', 'West');
dataset.fieldCaption('sales');
dataset.getMembers('region');
dataset.listFields();
// Build one grid without the stateful engine.
buildGrid(dataset, reportConfig);
// Use core parsing, aggregation, formatting, comparison, and date helpers.
compareValues('East', 'West');
createAggregator('average');
formatValue(1234.5);
formatDate(new Date(), 'yyyy-MM-dd');
resolveFormat({ name: 'currency' });
compileFormula("sum('revenue') - sum('cost')");
resolveCellStyle(8200, 'sales', []);
datePartValue(new Date(), 'quarter');
datePartCaption(2, 'quarter');
toDate('2026-04-15');
parseCsv('region,sales\nWest,1200');
inferFieldType(revenueRecords, 'sales');
normalizeValue('1200');

EventEmitter Methods

// Subscribe and receive an unsubscribe function.
const stop = engine.on('reportChange', (config) => {
  console.log(config);
});
// Subscribe for one event only.
engine.once('ready', () => {
  console.log('Pivot engine is ready.');
});
// Remove one handler, emit an event, or clear every subscription.
engine.off('reportChange', handler);
engine.emit('reportChange', engine.getConfiguration());
engine.clear();
stop();

PivotTable Methods

import { PivotTable } from '@pvotly/web';
const pivot = new PivotTable('#revenue-report', reportConfig);
// Force a new render or replace the report.
pivot.refresh();
pivot.getConfiguration();
pivot.setConfiguration(nextReportConfig);
// Subscribe or unsubscribe from pivot events.
const unsubscribe = pivot.on('cellClick', ({ cell }) => {
  console.log(cell.formatted);
});
pivot.off('cellClick', handler);
// Change display and interaction controls.
pivot.setTheme('dark');
pivot.setThemeTokens({ accent: '#0f766e' });
pivot.toggleFieldList(false);
await pivot.toggleFullscreen();
pivot.closeDialog();
// Export, print, and remove the widget.
pivot.exportTo('excel', { filename: 'quarterly-report', raw: true });
pivot.print('Quarterly Revenue');
pivot.destroy();
unsubscribe();

Export and UI Utility Methods

import {
  applyTheme,
  setThemeTokens,
  gridToMatrix,
  exportToCSV,
  exportToHTML,
  exportToJSON,
  exportToExcel,
  serializeExport,
  downloadExport,
  printGrid,
  renderGrid,
  mountFieldList,
  mountToolbar,
  openFilterDialog,
  openFormatDialog,
  openConditionalDialog,
} from '@pvotly/web';
const grid = engine.getGrid();
// Apply a theme or design tokens to a widget root.
applyTheme(document.querySelector('#revenue-report'), 'dark');
setThemeTokens(document.querySelector('#revenue-report'), {
  accent: '#0f766e',
});
// Serialize or download a computed grid.
gridToMatrix(grid, true);
exportToCSV(grid, { filename: 'revenue' });
exportToHTML(grid);
exportToJSON(grid);
exportToExcel(grid);
serializeExport(grid, 'csv');
downloadExport(grid, 'csv', { filename: 'revenue' });
printGrid(grid, 'Revenue');
// Build a custom widget composition from pvotly UI parts.
renderGrid(pivotContext, document.querySelector('#grid-host'));
mountFieldList(pivotContext, document.querySelector('#fields-host'));
mountToolbar(pivotContext, document.querySelector('#toolbar-host'));
openFilterDialog(pivotContext, 'region');
openFormatDialog(pivotContext, 'sales');
openConditionalDialog(pivotContext);

React Ref Methods

import { useRef } from 'react';
import { PivotTable, type PivotTableHandle } from '@pvotly/react';
function ReportActions() {
  const pivotRef = useRef<PivotTableHandle>(null);
  function exportCsv() {
    pivotRef.current?.getConfiguration();
    pivotRef.current?.setConfiguration(nextReportConfig);
    pivotRef.current?.exportTo('csv', { filename: 'report' });
    pivotRef.current?.print('Report');
    pivotRef.current?.refresh();
  }
  return <PivotTable ref={pivotRef} dataSource={{ data: revenueRecords }} />;
}

Events

const stopReady = pivot.on('ready', () => {
  console.log('The report is ready.');
});
const stopReportChange = pivot.on('reportChange', (config) => {
  console.log(config);
});
const stopDataChange = pivot.on('dataChange', ({ records }) => {
  console.log(records);
});
const stopCellClick = pivot.on('cellClick', ({ cell }) => {
  console.log(cell.formatted);
});
const stopCellDoubleClick = pivot.on('cellDoubleClick', ({ cell, records }) => {
  console.log(cell, records);
});
const stopFilterChange = pivot.on('filterChange', ({ field, filter }) => {
  console.log(field, filter);
});
const stopSortChange = pivot.on('sortChange', ({ field, direction }) => {
  console.log(field, direction);
});
const stopDrillThrough = pivot.on('drillThrough', ({ cell, records }) => {
  console.log(cell, records);
});
const stopError = pivot.on('error', ({ message, error }) => {
  console.error(message, error);
});
// Run each unsubscribe function when the host view unmounts.
[
  stopReady,
  stopReportChange,
  stopDataChange,
  stopCellClick,
  stopCellDoubleClick,
  stopFilterChange,
  stopSortChange,
  stopDrillThrough,
  stopError,
].forEach((stop) => stop());

Alternatives:

FAQs:

Q: Which data formats does pvotly accept?
A: The report accepts object records, an array-of-arrays matrix with a header row, raw CSV text, a remote HTTP source, or a custom fetcher. Use mapping when imported field types or captions need overrides.

Q: Why does the pivot table render without layout styles?
A: Import @pvotly/web/styles.css once in the application entry point. The stylesheet includes base widget styles and the built-in light and dark themes.

Q: How do I save a user’s field order, filters, and selected measures?
A: Call getConfiguration() after the report changes and store the returned serializable object. Pass that object to setConfiguration() when the report loads again.

Q: How should a large report start?
A: Enable options.virtualization and limit high-cardinality dimensions before users expand every hierarchy. Review the rendered row and column counts before adding several measures or deep date levels.

Q: How do I supply data as a CSV string at runtime?
A: Use the csv field in dataSource. The engine will parse it automatically. You can tweak delimiter, header presence, and type coercion with csvOptions.

Q: Does the headless engine work in Node.js or server-side environments?
A: Yes. @pvotly/core has zero dependencies and no DOM access. You can instantiate the engine, provide data, and call getGrid() in a Node process, worker, or test runner.

Q: Why do my date fields not appear as drillable hierarchies?
A: Add a mapping entry for the date field with type: 'date' and the desired dateParts, then reference them in the slice as fieldName.year, fieldName.month, etc.

Q: How do I persist and restore a user’s report layout?
A: Call engine.getConfiguration() to obtain a serializable object. Store it, and later pass it to engine.setConfiguration() or the <PivotTable /> dataSource/slice/options props.

You Might Be Interested In:


Leave a Reply