Vesture

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.

Spreadsheet

Preview

TAX() rate: 20%
A1Item
ABCDEF
1ItemPriceQtySubtotalTax
2Hosting$49.5012$594.00$118.80
3Design seats$30.005$150.00$30.00
4Support plan$199.001$199.00$39.80
5Total18$943.00$188.60
6Median 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

PropTypeDefaultDescription
valuestring[][]—Controlled raw cell text, value[row][col].
defaultValuestring[][][]Uncontrolled initial text.
onChange(value: string[][]) => void—Fires with the full grid after each committed edit or clear.
rows / columnsnumber—Grid size; never smaller than the data (minimum 10 × 6).
functionsRecord<string, (nums: number[]) => number>—Extra formula functions; names are case-insensitive.

Editing

PropTypeDefaultDescription
readOnlybooleanfalseBlocks all editing and clearing.
isCellEditable(pos: { row: number; col: number }) => boolean—Per-cell lock.
onActiveCellChange(pos) => void—Fires when the selection moves.

Display

PropTypeDefaultDescription
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—
columnHeadersstring[]—Labels replacing A, B, C… in the header row.
showHeadersbooleantrueRow numbers and column letters.
showFormulaBarbooleantrueThe cell-name box and raw-text bar above the grid.
columnWidthnumber110Default column width in px.
columnWidthsnumber[]—Per-column widths.
rowHeightnumber28
maxHeightnumber | string—Caps the height; the grid scrolls beyond it.