Formulas
Per-cell formulas with automatic dependency tracking, cycle detection, and live recalculation.
Any cell can hold a formula instead of a plain value — a string starting with = that references other cells
and recomputes automatically when they change, backed by a dependency graph (not a full recalculation on every
edit).
Writing a formula
A formula is just a cell value that starts with =. Typing one directly into an editable cell works exactly
like a spreadsheet — no separate "formula mode" to opt into:
api.transaction({ cells: [{ rowId: 'row-1', colField: 'total', value: '=[row-1:price] * [row-1:quantity]' }] });Cell references use [rowId:colField] — the grid's own row id and column field, not spreadsheet A1 notation.
Reference any cell in the grid, including ones in other columns or other rows:
api.transaction({ cells: [{ rowId: 'row-3', colField: 'variance', value: '=[row-3:actual] - [row-3:budget]' }] });Functions
SUM, AVERAGE, MIN, and MAX take a comma-separated argument list — each argument can be a cell reference,
a literal number, or a nested expression:
api.transaction({ cells: [{ rowId: 'row-1', colField: 'q1Total', value: '=SUM([row-1:jan], [row-1:feb], [row-1:mar])' }] });
api.transaction({ cells: [{ rowId: 'row-1', colField: 'avgDeal', value: '=AVERAGE([row-1:deal1], [row-1:deal2], [row-1:deal3])' }] });There's no range syntax (no SUM(A1:A10)) — list the cells explicitly, or compute the aggregate on your own data
and write the result with transaction if the set of contributing cells is large or dynamic.
Arithmetic supports +, -, *, /, parentheses, and unary minus, evaluated by a dedicated safe parser — never
eval().
Reading values
api.cell(rowId, field).value is always the resolved, computed value (.raw is the formula) — you never need to know whether a cell holds a
formula to read it:
const total = api.cell('row-1', 'total').value; // computed number, not "=[row-1:price] * [row-1:quantity]"Use the formula-specific methods when you need the formula itself, or want to set/clear one without going through a plain value edit:
api.getFormula('row-1', 'total'); // '=[row-1:price] * [row-1:quantity]' | undefined
api.hasFormula('row-1', 'total'); // boolean
api.transaction({ cells: [{ rowId: 'row-1', colField: 'total', value: '=[row-1:price] * [row-1:quantity]' }] });
api.transaction({ cells: [{ rowId: 'row-1', colField: 'total', value: 0 }] }); // a plain value replaces the formulaDependency tracking & recalculation
Every formula's references are parsed into a dependency graph. Editing [row-1:price] automatically invalidates
and recomputes total — and anything else that depends on total — without recomputing the rest of the grid.
Results are cached until a dependency actually changes.
Circular references
Setting a formula that would create a cycle (total depends on price, price depends on total) throws immediately, synchronously, from the
call that introduces it — the formula is rejected, not silently applied. A cycle that could somehow only appear at evaluation time surfaces as a
thrown error from that evaluation instead of an infinite loop.
Errors
An expression that fails to evaluate (a non-numeric reference used arithmetically, for example) resolves to a
string like #VALUE! (reason) rather than throwing — so a bad formula shows up visibly in the cell instead of
crashing the render:
api.cell('row-1', 'total').value; // '#VALUE! (Unexpected token...)' when the formula itself is malformedCombining with columns
Formulas work with any column — format still controls how the computed result reads as text, and parse still
reads what is pasted or typed (text starting with = is a formula):
const columns: ColumnDef<Person>[] = [
{
field: 'score',
header: 'Total',
format: ({ value }) => `$${Number(value).toFixed(2)}`,
},
];See Columns for the full column configuration surface, and Grid API
for the complete GridApi reference.
Last updated on