Sheetwrite evaluates formulas in the Rust/WASM calculation engine. Formula sources are persisted exactly as document values; evaluated results are cached for rendering, queries, export, and dependent formulas.
Sheetwrite intentionally implements a coherent spreadsheet subset. It does not claim full Google Sheets or Excel formula parity.
store.getFormula(address) returns the source. store.getCell(address).resolved returns the evaluated scalar or error sentinel. An unsupported function evaluates to #NAME?, but its source remains retrievable and persists through snapshots/XLSX round-trips so a later engine can evaluate it.
Supported formulas
The checked, versioned sheetwrite.formula-capabilities contract is the source of truth for function registration. Its generated reference lists every function in both engines, grouped by family, with aliases, exact signature and semantic profiles, implementation and evidence paths, source links, and behavior status. Do not infer support from an Excel, Google Sheets, or OpenFormula function with a similar name.
Function names are case-insensitive. Commas are the only documented argument separator. Interior omitted optional arguments are preserved (XLOOKUP(key, keys, results,, 0)). Locale-specific separators are not accepted.
Scalars, coercion, and errors
Persisted literals and evaluated formula results use string | number | boolean | null. Boolean literals entered as TRUE/FALSE, formula comparisons, XLSX boolean cells, snapshots, visible windows, clipboard output, and CSV/TSV output remain booleans end to end. Display and text export use uppercase TRUE and FALSE.
Sentinel
Meaning
#REF!
Missing sheet/cell reference or invalid reference structure.
#VALUE!
Wrong value type, malformed formula, invalid arity, or range-shape mismatch.
#DIV/0!
Division/modulo by zero or an empty criteria average.
#NAME?
Unknown function or unresolved named range.
#N/A
Lookup did not find a compatible value.
#NUM!
Non-finite numeric result, excessive recursion, or an oversized range.
#SPILL!
A dynamic result intersects content, another spill, a merge, validation/protection metadata, or a sheet boundary.
#CALC!
A supported array calculation has no result, such as FILTER without matches or an empty fallback.
A formula depends on datasource cells that have not loaded yet.
Errors are values for display and dependency propagation, not NaN or blank rendering. IFERROR can replace them. Unsupported or malformed source remains stored even when the result is an error.
Coercion follows these documented rules:
Arithmetic converts numeric text and booleans (TRUE = 1, FALSE = 0); nonnumeric text returns #VALUE!.
Direct scalar boolean/text arguments may be coerced by numeric functions. Text/booleans reached through a range are ignored by numeric aggregates, matching common spreadsheet behavior.
Empty scalar arithmetic behaves as zero. Empty range cells are skipped.
Comparisons are case-insensitive for text and use spreadsheet type ordering.
AND, OR, NOT, and IF accept booleans, numbers, and the text TRUE/FALSE.
LET bindings
LET(name1, value1, [name2, value2, …], calculation) supports lexical, case-insensitive local names. A name starts with an ASCII letter or _; subsequent characters may also contain ASCII digits or .. Inner LET bindings shadow outer bindings, and each value expression can see only earlier bindings.
Bindings are lazy. Only names reachable from calculation are expanded and evaluated, so an unused cell read, error, or NOW() does not become a dependency, error, or volatile marker. A used binding preserves ordinary formula dependency, spill, copy/fill, and structural-reference behavior. LET(x,1/0,7) therefore returns 7, while LET(x,A1,x+1) tracks A1.
One LET admits at most 126 bindings. Expansion admits at most 16,384 AST nodes across reachable bindings. Invalid name/arity/binding structure returns #VALUE!; expansion beyond the node ceiling returns #NUM!. In the full engine, a LET value can be a LAMBDA; see Full formula engine.
References, ranges, and named ranges
A1 cells (A1, $B12, AA$3), rectangular ranges (A1:B3), and cross-sheet references (Sales!E2, 'Sales 2026'!E2:E10) are supported. Absolute markers affect fill/structural rewriting; they do not change evaluation.
Named ranges are document operations and snapshot metadata:
A formula on summary resolves the sheet-scoped Revenue; formulas on other sheets resolve the workbook definition. Names are case-insensitive, must be formula-safe identifiers, and cannot look like A1 cells or boolean literals. Row/column insertions, removals, and moves rebase their rectangles. Deleting the complete target or its scope removes the definition; formulas then fall back to a workbook definition or evaluate to #NAME?.
Date/time semantics
Dates are numbers: whole days since the spreadsheet epoch, with a fractional day for time. Sheetwrite uses only the Excel 1900 date system on this path:
serial 0 maps to 1899-12-31
DATE(1900,1,1) = 1
serial 60 is retained as the compatibility-only, non-Gregorian date 1900-02-29
DATE(1900,3,1) = 61
DATE treats 1900 as a leap year for this serial boundary and normalizes month/day overflow; DATE(2024,13,1) is 2025-01-01
DATEVALUE is locale-neutral: it accepts the documented ISO yyyy-mm-dd form and fixed slash-delimited numeric forms; it never consults the host locale
Basic TEXT date patterns are yyyy-mm-dd, yyyy/mm/dd, mm/dd/yyyy, dd/mm/yyyy, m/d/yyyy, yyyy-mm-dd hh:mm, yyyy-mm-dd hh:mm:ss, hh:mm, and hh:mm:ss.
Text evaluation is host-locale neutral. Function argument separators stay commas; VALUE uses . decimal and , grouping, and NUMBERVALUE(text, [decimal_separator], [group_separator]) uses those same defaults unless the separators are supplied explicitly. Case conversion uses Unicode mappings, not browser locale. LEN, LEFT, RIGHT, and MID count Unicode scalar values rather than UTF-16 code units. Generated text is capped at 16 MiB and bounded searches at 4,000,000 steps; excess work returns #NUM!.
Cell information functions
FORMULATEXT(reference) returns the formula source of the top-left cell of reference. It returns #N/A when that cell holds no formula, and #VALUE! when the argument is not a reference.
ISFORMULA(reference) returns TRUE when the top-left cell of reference holds a formula, and FALSE otherwise.
HYPERLINK(link, [friendly_name]) returns friendly_name, or link when there is no friendly name. The grid does not open the link; use Grid.setHyperlink for links that open.
CELL(info_type, [reference]) supports the info types address, col, contents, row and type. Other info types, such as format or width, return #VALUE!, because number formats and column widths live in the host. Without reference, CELL describes the cell of the formula, as in LibreOffice.
Cells inside a spilled range and host-linked reference cells hold no formula. A formula that reads its own cell with these functions returns #CYCLE!.
Unsupported categories
Formula evaluation performs no network request and runs no custom JavaScript. RANDARRAY, network functions, external/live-data providers, arbitrary external workbook references, and cube/OLAP functions are unsupported. Examples include WEBSERVICE, GOOGLEFINANCE, IMPORT*, RTD, and CUBEVALUE. Database functions such as DSUM and LAMBDA helpers such as MAP are in the full engine only.
Unsupported names evaluate to #NAME? while preserving source for snapshots and interchange. There is no compatibility shim or side-effecting fallback. The generated unsupported-category ledger is derived from the versioned inventory.
Performance evidence
The engine guide shows function coverage, download size, and analysis workloads. Shared functions use the same implementation in both builds. Small timing differences do not show that one package is faster. Follow the performance evidence guide to capture and check measurements; resource ceilings are not benchmark results.
Point mode and reference rewriting
While editing a formula, clicking a cell inserts its A1 reference and dragging inserts a range. Relative references shift during fill; $-absolute axes remain fixed. Structural row/column edits rewrite direct and named references while preserving stable sheet identity.
A1 address helpers are exported as cellA1, colToA1, labelToCol, and rangeA1.
const grid: Gridconstgrid:Gridconstgrid:GridThe imperative core grid this controller owns.API reference →Grid.store: StoreinterfaceGrid {store:Store;}interfaceGrid {store:Store;}Store.applyTransaction(tx: Transaction, options?: TransactionApplicationOptions): ApplyTransactionResultStore.applyTransaction(tx: Transaction, options?: TransactionApplicationOptions): ApplyTransactionResultStore.applyTransaction(tx: Transaction, options?: TransactionApplicationOptions): ApplyTransactionResultApply a low-level storage transaction. This bypasses Grid read-only checks and Grid undo/redo history. Use Grid.applyTransaction for normal host-driven edits. Queued and flushed at a barrier — never reentrant.API reference →Transaction.patches: DocumentOp[]interfaceTransaction {patches:DocumentOp[];}interfaceTransaction {patches:DocumentOp[];}Ordered document operations submitted as one store commit.API reference →addr: CellAddresslet addr:CellAddresslet addr:CellAddressAPI reference →CellAddress.sheet: stringinterfaceCellAddress {sheet:string;}interfaceCellAddress {sheet:string;}Sheet identity used by the simple data-first adapter. Defaults to sheet1.API reference →CellAddress.row: numberinterfaceCellAddress {row:number;}interfaceCellAddress {row:number;}0-based row of the cell that holds the top-left corner.API reference →CellAddress.col: numberinterfaceCellAddress {col:number;}interfaceCellAddress {col:number;}0-based column of the cell that holds the top-left corner.API reference →value: CellValuelet value:CellValuelet value:CellValueAPI reference →src: stringlet src:stringlet src:stringfunction 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 →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 →GridOptions.workbook: WorkbookinterfaceGridOptions {workbook:Workbook;}interfaceGridOptions {workbook:Workbook;}Live 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.type: CellFormatinterfaceColumn {type:CellFormat;}interfaceColumn {type:CellFormat;}Controls cell input parsing and default value formatting for this column.API reference →Column.width: numberinterfaceColumn {width:number;}interfaceColumn {width:number;}Unzoomed column width in CSS pixels.API reference →GridOptions.data?: ColumnarData | undefinedinterfaceGridOptions {data?:ColumnarData|undefined;}interfaceGridOptions {data?:ColumnarData|undefined;}Eager column-major values loaded into workbook.activeSheet; use instead of datasource.API reference →ColumnarData.rowCount: numberinterfaceColumnarData {rowCount:number;}interfaceColumnarData {rowCount:number;}Row count for both in-memory and datasource-backed sheets.API reference →ColumnarData.columns: Record<string, ArrayLike<CellScalar | CellValue>>interfaceColumnarData {columns:Record<string, ArrayLike<CellScalar|CellValue>>;}interfaceColumnarData {columns:Record<string,ArrayLike<CellScalar|CellValue>>;}Exact column runs represented by every returned row.API reference →a: number[]let a:number[]let a:number[]result: null[]let result:null[]let result:null[]Search state after the replacement (matches re-scanned against the new data).API reference →const store: Storeconststore:Storeconststore:StoreAPI reference →namedRange: NamedRangeSnapshotlet namedRange:NamedRangeSnapshotlet namedRange:NamedRangeSnapshotAPI reference →NamedRangeSnapshot.name: stringinterfaceNamedRangeSnapshot {name:string;}interfaceNamedRangeSnapshot {name:string;}User-facing caption.API reference →NamedRangeSnapshot.range: RangeinterfaceNamedRangeSnapshot {range:Range;}interfaceNamedRangeSnapshot {range:Range;}Inclusive 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 →NamedRangeSnapshot.scope?: string | undefinedinterfaceNamedRangeSnapshot {scope?:string|undefined;}interfaceNamedRangeSnapshot {scope?:string|undefined;}Formula-context sheet whose local definition shadows the workbook definition.API reference →function shiftA1Refs(src: string, dRow: number, dCol: number): stringfunctionshiftA1Refs(src:string,dRow:number,dCol:number,):stringfunctionshiftA1Refs(src:string,dRow:number,dCol:number,):stringShift relative A1 references in a formula by (dRow, dCol) — used when a formula is filled into other cells. Absolute parts ($A, A$1) stay fixed, and tokens preceded by an alphanumeric (function names, identifiers) are skipped.API reference →