Data
SpreadsheetNew in 0.7.0
An editable grid with a formula engine — cell references, ranges, SUM / AVERAGE / MIN / MAX / COUNT / MEDIAN / PRODUCT, your own functions, per-cell formatting and locking.
SpreadsheetPreview
TAX() rate: 20%
A1Item
| A | B | C | D | E | F | |
|---|---|---|---|---|---|---|
| 1 | Item | Price | Qty | Subtotal | Tax | |
| 2 | Hosting | $49.50 | 12 | $594.00 | $118.80 | |
| 3 | Design seats | $30.00 | 5 | $150.00 | $30.00 | |
| 4 | Support plan | $199.00 | 1 | $199.00 | $39.80 | |
| 5 | Total | 18 | $943.00 | $188.60 | ||
| 6 | Median line | $199.00 | #DIV/0! | |||
| 7 | ||||||
| 8 | ||||||
| 9 | ||||||
| 10 |
Try: click a cell and type =SUM(B2:B4), change a quantity and watch totals recalculate, or edit D5 to =D5+1 for a circular reference. The last cell (=D5/0) shows an error value.
Usage
tsx
import { Spreadsheet } from "@vesture/react";
<Spreadsheet
defaultValue={[
["Price", "Qty", "Subtotal", "Tax"],
["19.99", "3", "=A2*B2", "=TAX(C2)"],
["5.50", "10", "=A3*B3", "=TAX(C3)"],
["Total", "", "=SUM(C2:C3)", "=SUM(D2:D3)"],
]}
functions={{ TAX: ([n]) => (n ?? 0) * 0.2 }}
formatCell={(v, { row }) => (typeof v === "number" && row > 0 ? `$${v.toFixed(2)}` : String(v))}
isCellEditable={({ row, col }) => row > 0 && col < 2}
/>Behavior
- The value is the raw text of every cell, string[][]: a string starting with = is a formula, everything else is a literal (numeric strings become numbers, right-aligned). Controlled via value / onChange, or uncontrolled via defaultValue.
- Formulas support + − × ÷, parentheses, unary minus, references like B3, ranges like A1:A5 and functions. Ranges skip text and blanks like a real spreadsheet; a bare reference to text is #VALUE!.
- Errors are values, not exceptions: #DIV/0!, #CYCLE! (circular reference), #REF!, #NAME?, #VALUE!, #ERR! (syntax). They propagate to dependent cells and are styled as errors.
- functions adds your own formula functions — { TAX: ([n]) => n * 0.2 } makes =TAX(C2) work. A function that throws shows #ERR! rather than crashing the grid.
- formatCell changes how a computed value is displayed (currency, percent, dates) without touching the stored text, and renderCell / getCellStyle / getCellClassName cover richer or conditional formatting. Editing always shows the raw text.
- readOnly locks everything; isCellEditable({ row, col }) locks cells individually (headers, computed columns).
- Keyboard: arrows, Home/End and Tab move; Enter or F2 starts editing; typing a character replaces the cell; Enter/Tab commit and move on; Escape cancels; Delete/Backspace clears.
Props
Data
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string[][] | — | Controlled raw cell text, value[row][col]. |
| defaultValue | string[][] | [] | Uncontrolled initial text. |
| onChange | (value: string[][]) => void | — | Fires with the full grid after each committed edit or clear. |
| rows / columns | number | — | Grid size; never smaller than the data (minimum 10 × 6). |
| functions | Record<string, (nums: number[]) => number> | — | Extra formula functions; names are case-insensitive. |
Editing
| Prop | Type | Default | Description |
|---|---|---|---|
| readOnly | boolean | false | Blocks all editing and clearing. |
| isCellEditable | (pos: { row: number; col: number }) => boolean | — | Per-cell lock. |
| onActiveCellChange | (pos) => void | — | Fires when the selection moves. |
Display
| Prop | Type | Default | Description |
|---|---|---|---|
| formatCell | (value: number | string, pos) => string | — | Display text for a computed value. |
| renderCell | ({ value, raw, pos, isActive }) => ReactNode | — | Replaces cell content. Return undefined to keep the default. |
| getCellStyle | (value, pos) => CSSProperties | undefined | — | Conditional formatting. |
| getCellClassName | (value, pos) => string | undefined | — | |
| columnHeaders | string[] | — | Labels replacing A, B, C… in the header row. |
| showHeaders | boolean | true | Row numbers and column letters. |
| showFormulaBar | boolean | true | The cell-name box and raw-text bar above the grid. |
| columnWidth | number | 110 | Default column width in px. |
| columnWidths | number[] | — | Per-column widths. |
| rowHeight | number | 28 | |
| maxHeight | number | string | — | Caps the height; the grid scrolls beyond it. |