Summarize a range or table into a pivot table with rows, columns, values and filters, edit it from the field list, and keep it through undo and sync.On this page
A pivot table summarizes a source range or a workbook table into groups. The
pivot definition is part of the document. The full engine calculates the
output, and updates it when source cells change: an edit updates only the
groups that the edited rows belong to. PIVOTBY is a formula and is separate:
it does not create a pivot table.
Insert and edit in the grid
Grids with the ribbon have Insert › PivotTable. It takes the selection, or
the table that holds it, as the source, and asks for the destination: a new
sheet or a cell. Then the field list opens in the side panel.
Drag fields into Rows, Columns, Values and Filters, or use the
actions menu on each field to move it with the keyboard.
A value field has an aggregation, and a row or column field has a sort order.
Each change is one undo step. Select a cell in a pivot's output, or
right-click it and choose PivotTable Fields, to open its field list again.
Hosts can do the same with grid.actions.showPivotFields().
With the default engine, Insert › PivotTable is disabled, and its tooltip
says that pivot tables need the full engine.
Define a pivot
A pivot belongs to the sheet that holds its anchor cell. Commit the complete
definition with setPivot. A second setPivot with the same id replaces the
definition. Each call is one undo entry.
A range with its header row, or { table: tableId } for a workbook table. A table source excludes the header and totals rows.
anchor
The top-left output cell on the owning sheet.
rows, columns
Source field indices that group the output rows and columns, outermost first. A field can be on one axis only.
values
Aggregated fields: SUM, COUNT, AVERAGE, MIN, MAX, COUNTA or DISTINCTCOUNT. These use the PIVOTBY semantics. SUM and AVERAGE round the exact sum one time. PIVOTBY rounds after each value, so the results can be different.
filters
Keep-lists: a source row contributes only if its value is in the list.
sort
The label order of axis fields. Fields without a key sort ascending.
rowTotals
The grand-total row at the bottom. Default true.
columnTotals
The grand-total column at the right. Default true.
subtotals
Subtotal rows and columns for multi-field axes. Default true.
layout
"compact" (one label column) or "tabular" (one label column per row field). Default "compact".
A field index is an offset in the source: column range.start.col + i, or
table column i. Invalid definitions are rejected before the document
changes. Limits: 1,000 pivots in a workbook, 256 entries in each list, and
10,000 filter values in one pivot.
Build your own field list
The side panel uses public methods, so a host can build its own field list. It
reads plain data and never reads engine structures.
getPivots(sheet?) returns copies of the definitions. It does not need the
engine.
getPivotFields(sheet, id) returns the source fields as
{ index, header, type }. The type is number, text, boolean, mixed
or blank.
getPivotResult(sheet, id) returns the output: status, anchor, rows,
cols, headerRows, labelCols and the cell values in row-major order. The
status is ok, saved, error (with error, for example #SPILL!), or
unavailable.
The store has the same methods. The full engine calculates pivots. Select it
as shown in Analysis formulas. With the
default engine, the document keeps, saves, undoes and synchronizes the same
definitions. It shows values saved by Excel without recalculating the pivot.
getPivotResult reports saved for those values, or unavailable when no
saved output exists. getPivotFields returns an empty list. setPivot removes
saved output because it no longer matches the changed definition.
Output
The output starts at the anchor and fills cells to the right and down, like a
spill. If source cells, formulas, another spill or another pivot are in the
way, the anchor shows #SPILL!. The pivot appears when the blocking cells are
cleared. Output that would cover its own source also shows #SPILL!.
Output cells are read-only. A local edit that writes a value into them is
rejected with a pivot-output mutation issue. Undo and redo are not checked:
a value that undo puts back blocks the pivot, and the anchor shows #SPILL!.
Row and column edits
Edits keep pivots up to date, with the same rules as formula references:
Rows or columns inserted or deleted around the source move or resize it.
Inside a range source, a deleted column removes its field from the pivot,
and the other field indices move with their columns.
When the whole source disappears (every source row or column is deleted, or
the source sheet or table is removed), the pivot is kept. Its source gets
invalid: true and keeps the old reference. The anchor shows #REF!.
When the row or column of the anchor is deleted, the pivot is kept. The
anchor moves to the first remaining row or column at the deleted position.
If a structural edit moves two anchors to the same cell, both definitions
remain saved and both results report #SPILL!. Moving one anchor away lets
the engine retry. An explicit local setPivot cannot choose an occupied
anchor.
Undo restores the exact definition from before the edit.
Differences from Excel
These Excel behaviors are not verified against Excel yet:
Excel refuses a delete that cuts through part of a PivotTable. Sheetwrite
accepts it and draws the output again at the new position.
Excel removes a PivotTable when whole rows or columns that cover it are
deleted. Sheetwrite keeps the pivot and moves its anchor.
Snapshots and collaboration
Definitions are in sheet.pivots of the workbook snapshot. Snapshot
validation checks pivot references after it reads all sheets and tables, so a
pivot can refer to a sheet that comes later in the snapshot. A reference to a
missing sheet or table is an error unless the source is invalid.
The operations are { op: "setPivot", sheet, pivot } and
{ op: "removePivot", sheet, id }. They synchronize like other document
operations. Two pending changes to the same pivot ID conflict during rebase.
releaseEngineMemory keeps the definitions and installs them in the new
engine.
XLSX saves
Represented pivots remain pivot definitions when an imported workbook is edited
and saved. Supported definitions are written as native XLSX pivot tables and
caches. Definitions that cannot be written natively are preserved in Sheetwrite
metadata, with a warning; Excel does not calculate that metadata-only output.
The reader captures Excel's rendered pivot values in pivot.savedOutput and
removes them from ordinary source cells. The default engine installs those
values as read-only calculated output, so cell reads and formulas can use
them. The full engine ignores saved values and calculates fresh output.
Loading either engine does not change the document. Snapshots and
collaboration carry the same saved output across engines. XLSX export writes
saved output back when no fresh rendered values replace it.
Unsupported or opaque imported pivots are different: their original parts are
preserved only while the workbook is unchanged. Editing the workbook drops those
opaque parts under the XLSX preservation policy (#179), with a warning. This does
not drop represented pivot definitions.
const grid: Gridconstgrid:Gridconstgrid:GridThe imperative core grid this controller owns.API reference →Grid.setPivot(pivot: PivotDefinition, sheet?: SheetId): ApplyTransactionResultGrid.setPivot(pivot: PivotDefinition, sheet?: SheetId): ApplyTransactionResultGrid.setPivot(pivot: PivotDefinition, sheet?: SheetId): ApplyTransactionResultInsert or replace a pivot as one undo entry. sheet defaults to the sheet that holds the pivot's ID, then to the active sheet. The pivot's savedOutput is removed, because it does not match a changed definition.API reference →PivotDefinition.id: stringinterfacePivotDefinition {id:string;}interfacePivotDefinition {id:string;}Stable identity, unique in the workbook.API reference →PivotDefinition.name: stringinterfacePivotDefinition {name:string;}interfacePivotDefinition {name:string;}User-facing caption.API reference →PivotDefinition.source: PivotSourceinterfacePivotDefinition {source:PivotSource;}interfacePivotDefinition {source:PivotSource;}Data the pivot summarizes.API reference →range: Rangelet range:Rangelet range:RangeInclusive table rectangle on one stable sheet ID.API reference →Range.sheet: stringinterfaceRange {sheet:string;}interfaceRange {sheet:string;}Sheet identity used by the simple data-first adapter. Defaults to sheet1.API reference →Range.start: {
row: number;
col: number;
}interfaceRange {start: {row:number;col:number;};}interfaceRange {start: {row:number;col:number;};}Zero-based workbook column index of the first key.API reference →row: numberlet row:numberlet row:number0-based row of the cell that holds the top-left corner.API reference →col: numberlet col:numberlet col:number0-based column of the cell that holds the top-left corner.API reference →Range.end: {
row: number;
col: number;
}interfaceRange {end: {row:number;col:number;};}interfaceRange {end: {row:number;col:number;};}Exclusive workbook column index after the last key.API reference →PivotDefinition.anchor: {
row: number;
col: number;
}interfacePivotDefinition {anchor: {row:number;col:number;};}interfacePivotDefinition {anchor: {row:number;col:number;};}Top-left output cell on the owning sheet. Deleting the anchor's row or column keeps the pivot and moves the anchor to the first surviving cell at the deletion position.API reference →PivotDefinition.rows: number[]interfacePivotDefinition {rows:number[];}interfacePivotDefinition {rows:number[];}Source field indices that group the output rows, outermost first.API reference →PivotDefinition.columns: number[]interfacePivotDefinition {columns:number[];}interfacePivotDefinition {columns:number[];}Source field indices that group the output columns, outermost first.API reference →PivotDefinition.values: PivotValueField[]interfacePivotDefinition {values:PivotValueField[];}interfacePivotDefinition {values:PivotValueField[];}Aggregated fields, in output order.API reference →PivotValueField.field: numberinterfacePivotValueField {field:number;}interfacePivotValueField {field:number;}Source field index that the value aggregates.API reference →PivotValueField.aggregation: PivotAggregationinterfacePivotValueField {aggregation:PivotAggregation;}interfacePivotValueField {aggregation:PivotAggregation;}How the field's values in each output cell combine.API reference →function createGrid(host: HTMLElement, opts: GridOptions): GridfunctioncreateGrid(host:HTMLElement,opts:GridOptions,):GridfunctioncreateGrid(host:HTMLElement,opts:GridOptions,):GridCreates and mounts an imperative Grid in the supplied host element.Exampleimport { createGrid, initSheetwrite } from"@sheetwrite/core";awaitinitSheetwrite();consthost= document.createElement("div");host.style.height ="400px";document.body.append(host);constgrid=createGrid(host, {workbook: {activeSheet: "sales",sheets: [{ id: "sales", name: "Sales", rowCount: 2,columns: [{ key: "amount", header: "Amount", type: "number", width: 120 }] }],},data: { rowCount: 2, columns: { amount: [12, 18] } },});grid.scrollToCell({ sheet: "sales", row: 1, col: 0 });// Call when the host view is removed.grid.destroy();See alsoGrid *See alsoGridOptionsAPI reference →function initSheetwrite(source?: BufferSource | URL | string | Request | WebAssembly.Module, engine?: SheetwriteEngine): Promise<void>functioninitSheetwrite(source?:BufferSource|URL|string|Request|WebAssembly.Module,engine?:SheetwriteEngine,):Promise<void>functioninitSheetwrite(source?:|BufferSource|URL|string|Request|WebAssembly.Module,engine?:SheetwriteEngine,):Promise<void>Load one WASM engine before createGrid. The default is @sheetwrite/wasm. Pass the @sheetwrite/formulas module as the second argument to select the full engine.Exampleimport { initSheetwrite } from"@sheetwrite/core";import*as formulas from"@sheetwrite/formulas";awaitinitSheetwrite(undefined, formulas);See also/docs/start/choose-an-engine/See alsocreateGridAPI reference →interface WorkbookinterfaceWorkbookinterfaceWorkbookLive workbook schema containing ordered sheets and the active sheet ID.API reference →const host: HTMLElement | nullconsthost:HTMLElement|nullconsthost:HTMLElement|nullvar document: Documentvar document:Documentvar document:Documentwindow.document returns a reference to the document contained in the window. MDN ReferenceAPI reference →ParentNode.querySelector<HTMLElement>(selectors: string): HTMLElement | null (+4 overloads)ParentNode.querySelector<HTMLElement>(selectors: string): HTMLElement |null (+4 overloads)ParentNode.querySelector<HTMLElement>(selectors: string): HTMLElement |null (+4 overloads)Returns the first element that is a descendant of node that matches selectors. MDN Referenceinterface HTMLElementinterfaceHTMLElementinterfaceHTMLElementThe HTMLElement interface represents any HTML element. MDN Referencevar Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+2 overloads)var Error:ErrorConstructornew (message?:string, options?:ErrorOptions) =>Error (+2 overloads)var Error:ErrorConstructornew (message?:string, options?:ErrorOptions) =>Error (+2 overloads)const host: HTMLElementconsthost:HTMLElementconsthost:HTMLElementElementCSSInlineStyle.style: CSSStyleDeclarationinterfaceElementCSSInlineStyle {style:CSSStyleDeclaration;}interfaceElementCSSInlineStyle {style:CSSStyleDeclaration;}MDN ReferenceAPI reference →CSSStyleDeclaration.height: stringinterfaceCSSStyleDeclaration {height:string;}interfaceCSSStyleDeclaration {height:string;}MDN ReferenceAPI reference →var undefinedvar undefinedvar undefinedconst workbook: Workbookconstworkbook:Workbookconstworkbook:WorkbookLive workbook schema adopted by the store and updated by document operations.API reference →Workbook.activeSheet: stringinterfaceWorkbook {activeSheet:string;}interfaceWorkbook {activeSheet:string;}Active sheet ID and initial tab presented when the grid is created.API reference →Workbook.sheets: Sheet[]interfaceWorkbook {sheets:Sheet[];}interfaceWorkbook {sheets:Sheet[];}Sheets in display/tab order.API reference →Sheet.id: stringinterfaceSheet {id:string;}interfaceSheet {id:string;}Stable identifier, unique within the workbook and used by every cell address.API reference →Sheet.name: stringinterfaceSheet {name:string;}interfaceSheet {name:string;}User-facing sheet name shown in tabs and workbook exports.API reference →Sheet.rowCount: numberinterfaceSheet {rowCount:number;}interfaceSheet {rowCount:number;}Row count for both in-memory and datasource-backed sheets.API reference →Sheet.columns: Column[]interfaceSheet {columns:Column[];}interfaceSheet {columns:Column[];}Ordered schema; array positions are the zero-based column coordinates.API reference →Column.key: stringinterfaceColumn {key:string;}interfaceColumn {key:string;}Non-empty key, unique within the sheet, used to map input and datasource values.API reference →Column.header: stringinterfaceColumn {header:string;}interfaceColumn {header:string;}Schema label used by table exports and data-grid presentation headers.API reference →Column.width: numberinterfaceColumn {width:number;}interfaceColumn {width:number;}Unzoomed column width in CSS pixels.API reference →Column.type: CellFormatinterfaceColumn {type:CellFormat;}interfaceColumn {type:CellFormat;}Controls cell input parsing and default value formatting for this column.API reference →Array<string>.map<{
key: string;
header: string;
width: number;
type: "text";
}>(callbackfn: (value: string, index: number, array: string[]) => {
key: string;
header: string;
width: number;
type: "text";
}, thisArg?: any): {
key: string;
header: string;
width: number;
type: "text";
}[]Array<string>.map<{key:string;header:string;width:number;type:"text";}>(callbackfn: (value:string, index:number, array:string[]) => {key:string;header:string;width:number;type:"text";}, thisArg?:any): {key:string;header:string;width:number;type:"text";}[]Array<string>.map<{key:string;header:string;width:number;type:"text";}>(callbackfn: (value:string, index:number, array:string[]) => {key:string;header:string;width:number;type:"text";}, thisArg?:any): {key:string;header:string;width:number;type:"text";}[]Calls a defined callback function on each element of an array, and returns an array that contains the results.@param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.key: stringlet key:stringlet key:stringNon-empty key, unique within the sheet, used to map input and datasource values.API reference →header: stringlet header:stringlet header:stringSchema label used by table exports and data-grid presentation headers.API reference →width: numberlet width:numberlet width:numberUnzoomed column width in CSS pixels.API reference →type: "text"let type:"text"let type:"text"Controls cell input parsing and default value formatting for this column.API reference →type const = "text"typeconst="text"typeconst="text"GridOptions.workbook: WorkbookinterfaceGridOptions {workbook:Workbook;}interfaceGridOptions {workbook:Workbook;}Live workbook schema adopted by the store and updated by document operations.API reference →const rows: (string | number)[][]constrows: (string|number)[][]constrows: (string|number)[][]Source field indices that group the output rows, outermost first.API reference →Grid.applyTransaction(transaction: GridTransaction): ApplyTransactionResultGrid.applyTransaction(transaction: GridTransaction): ApplyTransactionResultGrid.applyTransaction(transaction: GridTransaction): ApplyTransactionResultApply arbitrary patches as one undoable Grid commit. No-op when read-only. Use Store.applyTransaction only for low-level writes that intentionally bypass Grid history and policy.Exampleimporttype { Grid } from"@sheetwrite/core";functionsetFirstCell(grid:Grid) {return grid.applyTransaction({ patches: [{ op: "set", addr: { sheet: grid.getActiveSheet(), row: 0, col: 0 },value: { kind: "literal", value: 42 } },] });}See alsoTransactionAPI reference →GridTransaction.patches: DocumentOp[]interfaceGridTransaction {patches:DocumentOp[];}interfaceGridTransaction {patches:DocumentOp[];}Ordered document operations submitted as one store commit.API reference →Array<(string | number)[]>.flatMap<{
op: "set";
addr: {
sheet: string;
row: number;
col: number;
};
value: {
kind: "literal";
value: string | number;
};
}, undefined>(callback: (this: undefined, value: (string | number)[], index: number, array: (string | number)[][]) => {
op: "set";
addr: {
sheet: string;
row: number;
col: number;
};
value: {
kind: "literal";
value: string | number;
};
} | readonly {
op: "set";
addr: {
sheet: string;
row: number;
col: number;
};
value: {
kind: "literal";
value: string | number;
};
}[], thisArg?: undefined): {
op: "set";
addr: {
sheet: string;
row: number;
col: number;
};
value: {
kind: "literal";
value: string | number;
};
}[]Array<(string | number)[]>.flatMap<{op:"set";addr: {sheet:string;row:number;col:number;};value: {kind:"literal";value:string|number;};}, undefined>(callback: (this:undefined, value: (string|number)[], index:number, array: (string|number)[][]) => {op:"set";addr: {sheet:string;row:number;col:number;};value: {kind:"literal";value:string|number;};} |readonly {op:"set";addr: {sheet:string;row:number;col:number;};value: {kind:"literal";value:string|number;};}[], thisArg?:undefined): {op:"set";addr: {sheet:string;row:number;col:number;};value: {kind:"literal";value:string|number;};}[]Array<(string | number)[]>.flatMap<{op:"set";addr: {sheet:string;row:number;col:number;};value: {kind:"literal";value:string|number;};}, undefined>(callback: (this:undefined, value: (string|number)[], index:number, array: (string|number)[][]) => {op:"set";addr: {sheet:string;row:number;col:number;};value: {kind:"literal";value:string|number;};} |readonly {op:"set";addr: {sheet:string;row:number;col:number;};value: {kind:"literal";value:string|number;};}[], thisArg?:undefined): {op:"set";addr: {sheet:string;row:number;col:number;};value: {kind:"literal";value:string|number;};}[]Calls a defined callback function on each element of an array. Then, flattens the result into a new array. This is identical to a map followed by flat with depth 1.@param callback A function that accepts up to three arguments. The flatMap method calls the callback function one time for each element in the array.@param thisArg An object to which the this keyword can refer in the callback function. If thisArg is omitted, undefined is used as the this value.cells: (string | number)[]let cells: (string|number)[]letcells: (string|number)[]Array<string | number>.map<{
op: "set";
addr: {
sheet: string;
row: number;
col: number;
};
value: {
kind: "literal";
value: string | number;
};
}>(callbackfn: (value: string | number, index: number, array: (string | number)[]) => {
op: "set";
addr: {
sheet: string;
row: number;
col: number;
};
value: {
kind: "literal";
value: string | number;
};
}, thisArg?: any): {
op: "set";
addr: {
sheet: string;
row: number;
col: number;
};
value: {
kind: "literal";
value: string | number;
};
}[]Array<string | number>.map<{op:"set";addr: {sheet:string;row:number;col:number;};value: {kind:"literal";value:string|number;};}>(callbackfn: (value:string|number, index:number, array: (string|number)[]) => {op:"set";addr: {sheet:string;row:number;col:number;};value: {kind:"literal";value:string|number;};}, thisArg?:any): {op:"set";addr: {sheet:string;row:number;col:number;};value: {kind:"literal";value:string|number;};}[]Array<string | number>.map<{op:"set";addr: {sheet:string;row:number;col:number;};value: {kind:"literal";value:string|number;};}>(callbackfn: (value:string|number, index:number, array: (string|number)[]) => {op:"set";addr: {sheet:string;row:number;col:number;};value: {kind:"literal";value:string|number;};}, thisArg?:any): {op:"set";addr: {sheet:string;row:number;col:number;};value: {kind:"literal";value:string|number;};}[]Calls a defined callback function on each element of an array, and returns an array that contains the results.@param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.value: string | numberlet value:string|numberlet value:string|numbertype const = "set"typeconst="set"typeconst="set"addr: {
sheet: string;
row: number;
col: number;
}let addr: {sheet:string;row:number;col:number;}let addr: {sheet:string;row:number;col:number;}sheet: stringlet sheet:stringlet sheet:stringSheet identity used by the simple data-first adapter. Defaults to sheet1.API reference →value: {
kind: "literal";
value: string | number;
}let value: {kind:"literal";value:string|number;}let value: {kind:"literal";value:string|number;}type const = "literal"typeconst="literal"typeconst="literal"PivotValueField.name?: string | undefinedinterfacePivotValueField {name?:string|undefined;}interfacePivotValueField {name?:string|undefined;}Custom caption; without it the output shows the default caption.API reference →PivotDefinition.filters?: PivotFilter[] | undefinedinterfacePivotDefinition {filters?:PivotFilter[] |undefined;}interfacePivotDefinition {filters?:PivotFilter[] |undefined;}Keep-lists that limit which source rows contribute; at most one per field.API reference →PivotFilter.field: numberinterfacePivotFilter {field:number;}interfacePivotFilter {field:number;}Source field index that the value aggregates.API reference →PivotFilter.values: CellScalar[]interfacePivotFilter {values:CellScalar[];}interfacePivotFilter {values:CellScalar[];}Numeric cells; text, booleans, errors and empty cells are null (a gap).API reference →PivotDefinition.sort?: PivotSort[] | undefinedinterfacePivotDefinition {sort?:PivotSort[] |undefined;}interfacePivotDefinition {sort?:PivotSort[] |undefined;}Label order of grouped fields; each key names a field that groups its axis.API reference →PivotSort.axis: "rows" | "columns"interfacePivotSort {axis:"rows"|"columns";}interfacePivotSort {axis:"rows"|"columns";}API reference →PivotSort.field: numberinterfacePivotSort {field:number;}interfacePivotSort {field:number;}Source field index that the value aggregates.API reference →PivotSort.descending: booleaninterfacePivotSort {descending:boolean;}interfacePivotSort {descending:boolean;}API reference →Grid.removePivot(id: string, sheet?: SheetId): ApplyTransactionResultGrid.removePivot(id: string, sheet?: SheetId): ApplyTransactionResultGrid.removePivot(id: string, sheet?: SheetId): ApplyTransactionResultRemove a pivot from sheet (default: the sheet that holds it) as one undo entry.API reference →