svelte-pivottable

March 30, 2026 · View on GitHub

svelte-pivottable is a Svelte 5 pivot table library with drag-and-drop data exploration, grouping, and subtotals — similar to the pivot table experience in older versions of Microsoft Excel.

It is a Svelte port of react-pivottable-grouping, itself a fork of react-pivottable by Nicolas Kruchten, which is a React port of his jQuery-based PivotTable.js.

What does it do?

svelte-pivottable lets you summarize a dataset into a cross-tabulated table or a Plotly.js chart using a drag-and-drop interface. You can move attributes between rows, columns, filters, and aggregation slots interactively.

A live demo is available here.

screencap

Installation

npm install svelte-pivottable

Basic usage

Interactive UI with table output

PivotTableUI is the full drag-and-drop component. It manages its own UI state but delegates data rendering to PivotTable.

<script>
    import { PivotTableUI } from 'svelte-pivottable';

    const data = [
        { name: 'Alice', department: 'Engineering', salary: 95000 },
        { name: 'Bob',   department: 'Marketing',   salary: 72000 },
        { name: 'Carol', department: 'Engineering', salary: 105000 },
        // ...
    ];
</script>

<PivotTableUI {data} rows={['department']} cols={[]} />

Data can also be provided as an array of arrays (first row is headers):

<script>
    import { PivotTableUI } from 'svelte-pivottable';

    const data = [
        ['name', 'department', 'salary'],
        ['Alice', 'Engineering', 95000],
        ['Bob',   'Marketing',   72000],
    ];
</script>

<PivotTableUI {data} />

Non-interactive snapshot rendering

PivotTable renders a saved configuration without any drag-and-drop UI. Use it for embedding fixed pivot views in dashboards.

<script>
    import { PivotTable, TableRenderers, aggregators } from 'svelte-pivottable';

    const data = [ /* ... */ ];
</script>

<PivotTable
    {data}
    rows={['department']}
    cols={['year']}
    aggregator={aggregators['Sum']}
    vals={['salary']}
    renderers={TableRenderers}
    rendererName="Table Heatmap"
/>

Renderers

Table renderers

TableRenderers is the default renderer set. Import it from svelte-pivottable or the svelte-pivottable/TableRenderers subpath.

KeyDescription
TablePlain cross-tabulation table
Table HeatmapTable with full color scale across all cells
Table Col HeatmapHeatmap scaled per column
Table Row HeatmapHeatmap scaled per row
TsvExportTab-separated values export view
<script>
    import { PivotTable, TableRenderers } from 'svelte-pivottable';
</script>

<PivotTable {data} rows={['region']} cols={['quarter']} renderers={TableRenderers} rendererName="Table Col Heatmap" />

Plotly renderers

Plotly.js is injected as a dependency to avoid mandatory bundling. Pass the Plotly object to PlotlyRenderers() to get the renderer map.

<script>
    import { PivotTableUI, TableRenderers, PlotlyRenderers } from 'svelte-pivottable';
    import Plotly from 'plotly.js';

    const renderers = { ...TableRenderers, ...PlotlyRenderers(Plotly) };
</script>

<PivotTableUI {data} {renderers} />

If you load Plotly via a <script> tag rather than bundling it:

<script>
    import { onMount } from 'svelte';
    import { PivotTableUI, TableRenderers, PlotlyRenderers } from 'svelte-pivottable';

    let renderers = { ...TableRenderers };

    onMount(() => {
        renderers = { ...TableRenderers, ...PlotlyRenderers(window.Plotly) };
    });
</script>

<svelte:head>
    <script src="https://cdn.plot.ly/plotly-basic-latest.min.js"></script>
</svelte:head>

<PivotTableUI {data} {renderers} />

Aggregators

Built-in aggregators

The aggregators export is a ready-to-use map of aggregation functions. Pass it (or a subset) to PivotTableUI via the aggregators prop, or use individual entries as the aggregator prop on PivotTable.

<script>
    import { PivotTable, aggregators } from 'svelte-pivottable';
</script>

<!-- Render a Sum table directly -->
<PivotTable
    {data}
    rows={['department']}
    cols={['year']}
    aggregator={aggregators['Sum']}
    vals={['salary']}
/>

Available built-in aggregators:

KeyDescription
CountNumber of records
Count Unique ValuesNumber of distinct values
List Unique ValuesComma-separated distinct values
SumSum of a numeric field
Integer SumSum formatted as integer
AverageMean
MedianMedian
Sample VarianceSample variance
Sample Standard DeviationSample standard deviation
MinimumMinimum value
MaximumMaximum value
FirstFirst value seen
LastLast value seen
Sum over SumRatio of two sums
Sum as Fraction of TotalEach cell as % of grand total
Sum as Fraction of RowsEach cell as % of its row total
Sum as Fraction of ColumnsEach cell as % of its column total
Count as Fraction of TotalCount as % of grand total
Count as Fraction of RowsCount as % of row total
Count as Fraction of ColumnsCount as % of column total

Limiting the aggregator dropdown

Pass a subset of aggregators to restrict what appears in the UI:

<script>
    import { PivotTableUI, aggregators } from 'svelte-pivottable';

    const myAggregators = {
        Count:   aggregators['Count'],
        Sum:     aggregators['Sum'],
        Average: aggregators['Average'],
    };
</script>

<PivotTableUI {data} aggregators={myAggregators} />

Custom aggregators with aggregatorTemplates

Use aggregatorTemplates as building blocks for your own aggregation logic:

<script>
    import { PivotTableUI, aggregators, aggregatorTemplates, fmtInt } from 'svelte-pivottable';

    // Count records where a field exceeds a threshold
    const countAbove = (threshold) =>
        ([attr]) =>
            () => ({
                count: 0,
                push(record) {
                    if (Number(record[attr]) > threshold) this.count++;
                },
                value() { return this.count; },
                format: fmtInt,
                numInputs: 1,
            });

    const myAggregators = {
        ...aggregators,
        'High Earners': countAbove(100000),
    };
</script>

<PivotTableUI {data} aggregators={myAggregators} aggregatorName="High Earners" vals={['salary']} />

Derived attributes

Use derivedAttributes to compute new fields on the fly without modifying the source data. The built-in derivers helper provides common patterns:

<script>
    import { PivotTableUI, derivers } from 'svelte-pivottable';

    const data = [
        { name: 'Alice', born: '1990-06-15', salary: 95000 },
        // ...
    ];

    const derivedAttributes = {
        // Bin a numeric field into buckets of 10
        'Salary Band': derivers.bin('salary', 10000),

        // Extract year from a date string
        'Birth Year': derivers.dateFormat('born', '%y'),

        // Fully custom computed field
        'Senior': (record) => record.salary > 100000 ? 'Yes' : 'No',
    };
</script>

<PivotTableUI {data} {derivedAttributes} rows={['Salary Band']} />

Sorting

Custom sort order with sortAs

Use sortAs to enforce a fixed value ordering (e.g. month names, priority levels):

<script>
    import { PivotTableUI, sortAs } from 'svelte-pivottable';

    const sorters = {
        quarter: sortAs(['Q1', 'Q2', 'Q3', 'Q4']),
        priority: sortAs(['Low', 'Medium', 'High', 'Critical']),
    };
</script>

<PivotTableUI {data} {sorters} cols={['quarter']} />

Grouping and subtotals

Set grouping={true} to display hierarchical row and column groups with subtotals at each level. This is the main feature added over the original react-pivottable.

<script>
    import { PivotTableUI } from 'svelte-pivottable';
</script>

<PivotTableUI
    {data}
    rows={['continent', 'country', 'city']}
    cols={['year', 'quarter']}
    grouping={true}
    rowGroupBefore={true}
    colGroupBefore={false}
    compactRows={true}
/>
PropTypeDefaultDescription
groupingbooleanfalseEnable hierarchical grouping with subtotals
compactRowsbooleantrueRender row group headers inline rather than as separate rows
rowGroupBeforebooleantrueShow row subtotals above their children
colGroupBeforebooleanfalseShow column subtotals to the left of their children

All properties

The component stack passes props downward: PivotTableUI → PivotTable → Renderer → PivotData.

LayerPropTypeDefaultDescription
PivotDatadataarray or function(required)Dataset to summarize — see accepted formats
PivotDatarowsstring[][]Attribute names to pre-populate in the row area
PivotDatacolsstring[][]Attribute names to pre-populate in the column area
PivotDatavalsstring[][]Attribute names passed to the aggregator function
PivotDataaggregatorfunctionaggregators['Count']Aggregation function (see aggregators)
PivotDatavalueFilterRecord<string, Record<string, boolean>>{}Values to exclude per attribute; populates the filter menus
PivotDatasortersobject or function{}Per-attribute sort functions; defaults to natural sort
PivotDatarowOrderstring"key_a_to_z"Row sort order: key_a_to_z, value_a_to_z, or value_z_to_a
PivotDatacolOrderstring"key_a_to_z"Column sort order: key_a_to_z, value_a_to_z, or value_z_to_a
PivotDataderivedAttributesobject of functions{}Functions that add computed fields to each record
PivotDatagroupingbooleanfalseEnable subtotal rows and columns
PivotDatarowGroupBeforebooleantruePlace row subtotals above their group
PivotDatacolGroupBeforebooleanfalsePlace column subtotals to the left of their group
PivotTablerenderersobjectTableRenderersMap of renderer name → Svelte component
PivotTablerendererNamestringfirst key in renderersWhich renderer to use
PivotTableUIaggregatorsobjectaggregators from UtilitiesMap of aggregator name → function, shown in the dropdown
PivotTableUIaggregatorNamestringfirst key in aggregatorsWhich aggregator to use initially
PivotTableUIhiddenAttributesstring[][]Attributes to hide from the entire UI
PivotTableUIhiddenFromAggregatorsstring[][]Attributes to hide from the aggregator field picker
PivotTableUIhiddenFromDragDropstring[][]Attributes to hide from the drag-and-drop zone
PivotTableUImenuLimitnumber500Maximum number of values to show in a filter menu
PivotTableUIunusedOrientationCutoffnumber85Total character length of attribute names above which unused attributes are shown vertically instead of horizontally. 0 = always vertical, Infinity = always horizontal
PivotTableUIcompactRowsbooleantrueCompact rendering for grouped row headers

Accepted formats for data

Array of objects

const data = [
    { attr1: 'value1', attr2: 'value2' },
    { attr1: 'value3', attr2: 'value4' },
];

Array of arrays

First row is treated as headers. Compatible with CSV parser output (e.g. PapaParse).

const data = [
    ['attr1', 'attr2'],
    ['value1', 'value2'],
    ['value3', 'value4'],
];

Callback function

The function receives a record callback and calls it once per record. Useful for streaming or lazy data sources.

const data = (callback) => {
    callback({ attr1: 'value1', attr2: 'value2' });
    callback({ attr1: 'value3', attr2: 'value4' });
};

Missing attributes and null values are treated as the string "null" in all formats.

Component architecture

PivotTableUI        — stateful drag-and-drop UI
  └─ PivotTable     — stateless renderer selector
       └─ Renderer  — TableRenderer, PlotlyRenderer, TSVExportRenderer, etc.
            └─ PivotData  — aggregation, filtering, sorting, subtotal tree

License

MIT