Editing Excel without Office: what a browser formula engine really supports
Updated 2026-08-09
Open an .xlsx file in a browser-based editor and two separate pieces of machinery go to work: a file parser (SheetJS in our case) that unpacks the workbook, and a formula engine (fast-formula-parser) that re-evaluates every formula from scratch, because cached results in the file go stale the moment you edit a cell. Neither piece is Excel. Excel implements roughly 500 worksheet functions plus a recalculation chain, number formatting, and forty years of edge cases. A browser engine implements a subset, and the only accurate way to describe that subset is to test it, function by function, against the shipping code.
That is what this guide is: a tested inventory of the formula engine behind our Excel editor, including the parts that do not work. If you know precisely where the edges are, a browser editor is genuinely useful. If you do not, you will eventually type a formula from 1985 and wonder why it returns an error code.
How a formula travels from file to result
On load, the parser reads the workbook with formula extraction enabled. Each cell contributes either its plain value or, if it holds a formula, the formula source text prefixed with = — so formulas stay live and editable instead of being frozen at whatever value Excel last cached. The working representation is deliberately primitive: a grid of raw strings. That single decision explains most of what follows, both the good (nothing is hidden, undo/redo is a snapshot of strings) and the bad (no formatting layer exists at all).
On every edit, the entire sheet recomputes. The engine parses each formula's cell references, builds a dependency graph among formula cells, orders it with Kahn's topological sort, and evaluates in that order — so a cell is only computed after everything it references. Any cell that cannot be ordered is part of a cycle and renders as #CIRCULAR!, including the degenerate one-cell cycle of a formula that references itself.
A1: 2 B1: =A1*10 → 20 C1: =B1+5 → 25 (computed after B1, regardless of cell position) D1: =D1+1 → #CIRCULAR! E1: =F1 F1: =E1 → #CIRCULAR! in both cells
Full recomputation per keystroke is the simple-and-correct choice, not the fast one. Excel maintains an incremental calculation chain and only recomputes dirty cells; this engine recomputes everything. For a hand-edited sheet of a few thousand cells that is imperceptible. If you paste fifty thousand rows of formulas, you will feel it.
The function list, tested cell by cell
fast-formula-parser 1.0.19 registers 342 function names, but a registered name is not an implemented function. Dozens of entries in the library are literally empty function bodies that throw "not implemented" the moment they are called — and the casualties include functions you would never suspect. MAX and MIN, of all things, are empty stubs in the library source (statistical.js, lines 132–150), which is why an early build of this editor answered =MAX(A1:A10) with an error. We now register our own implementations for the eleven most-used of them at parser construction, so MAX, MIN, MEDIAN, COUNTA, UPPER, MATCH, SUBSTITUTE, TEXTJOIN, CHOOSE, LARGE and SMALL evaluate here even though the upstream library cannot compute them. Everything still stubbed maps to #NAME? in the cell, the same code Excel uses for an unknown name.
| Status | Functions (each one executed in a test grid) | What you see |
|---|---|---|
| Works | SUM, AVERAGE, COUNT, COUNTIF, AVERAGEIF, SUMIF, SUMPRODUCT, PRODUCT, IF, IFERROR, AND, OR, NOT, VLOOKUP, HLOOKUP, INDEX, ROUND, ROUNDUP, ABS, MOD, INT, SQRT, POWER, CONCATENATE, CONCAT, LEFT, RIGHT, MID, LEN, TRIM, LOWER, PROPER, EXACT, FIND, SEARCH, REPLACE, REPT, TEXT, DATE, DAYS, EOMONTH, TODAY, plus arithmetic and comparison operators | The computed value |
| Missing upstream, implemented by us | MAX, MIN, MEDIAN, COUNTA, UPPER, MATCH, SUBSTITUTE, TEXTJOIN, CHOOSE, LARGE, SMALL | The computed value |
| Registered but still an empty stub | SWITCH, SUMIFS, COUNTIFS, SUBTOTAL, MAXIFS, RANK, VALUE, and the STDEV / VAR / PERCENTILE / QUARTILE / MODE families | #NAME? |
| Absent entirely (modern dynamic arrays) | XLOOKUP, FILTER, UNIQUE, SEQUENCE, LET, LAMBDA | #NAME? |
Two things soften what remains. First, the failure is almost always loud: an unimplemented function gives you an error code in the cell, not a wrong number. Two paths do produce a silently wrong value, and both are worth knowing before you trust a figure — a cross-sheet reference such as Sheet2!A1 is resolved against the active sheet, because the engine passes a single fixed sheet name for every cell, and a formula that returns an array is collapsed to its first value rather than spilled. Second, the formula text itself is preserved — a cell showing #NAME? still holds =STDEV(A1:A10) as its source, and that source is written into the .xlsx on export, where Excel or LibreOffice will evaluate it correctly. The browser engine failing to compute a function does not corrupt the formula.
The four error codes and what each actually means
- #NAME? — the function is not implemented in the browser engine, or the name genuinely does not exist. The formula source is kept intact either way.
- #CIRCULAR! — the cell is part of a reference cycle. This code is specific to this tool; Excel instead shows a warning dialog and a zero.
- #DIV/0!, #N/A, #VALUE! — real Excel-semantics errors propagated from the library. Verified directly: =1/0 renders #DIV/0!, =NA() renders #N/A, and =IFERROR(1/0, "fallback") correctly catches the error and returns the fallback.
- #ERROR! — the catch-all when evaluation throws something the engine does not recognize, such as a malformed expression.
- One divergence worth knowing: SEARCH here is case-sensitive, so =SEARCH("L","hello") returns #VALUE! where Excel would return 3. In Excel, FIND is the case-sensitive one and SEARCH ignores case; the library backing this tool treats both as case-sensitive. Lowercase the needle, or use a helper column, if you are porting a sheet that relies on the Excel behavior.
Dates are serial numbers here — there is no formatting layer
Excel stores dates as serial numbers (days since December 30, 1899) and displays them through a number-format layer. This tool has the first half and not the second. Type =TODAY() on August 9, 2026 and the cell shows 46243 — the correct serial, unformatted. A date-typed cell in an uploaded file arrives the same way, because the grid stores the underlying value, not the display string. Date arithmetic still works exactly as in Excel: subtracting two date cells gives days between them, and =DATE(2026,8,9) returns the same 46243. Just expect to apply display formatting later, in Excel, or feed the serials to whatever consumes the CSV.
Export fidelity: what .xlsx keeps and what .csv throws away
| Aspect | .xlsx export | .csv export |
|---|---|---|
| Formulas | Written as live formulas with a cached computed value; recalculate normally in Excel | Flattened to their computed results |
| Multiple sheets | All sheets included | Active sheet only |
| Cells showing errors | Formula kept; error text stored as the cached value | The error code as text |
| Formatting, column widths, merged cells, charts, pivot tables | Lost — never loaded into the grid in the first place | Lost |
| Quoting | Not applicable | Every field quoted, internal quotes doubled (RFC 4180-style) |
One SheetJS behavior forced a design decision worth knowing about: when writing a workbook, SheetJS silently drops any formula cell that has no cached value — we verified this with a direct write/read round-trip, where a bare formula cell simply vanished from the output file. That is why the tool runs the formula engine one final time at export and writes each formula together with its computed value. It is also why the exported file opens in Excel showing sensible numbers immediately rather than zeros pending a recalculation.
The bigger caveat is the last table row. Exporting builds a new workbook from the string grid, so anything the grid never captured — cell colors, number formats, merged regions, charts, comments — is absent from the exported copy even though it was present in your original file. Keep the original. Treat the export as a values-and-formulas extract, not a re-save.
Where this fits in a real workflow
The realistic scope: inspecting a spreadsheet on a machine without Office, fixing a handful of values or formulas, pulling a sheet out to CSV, or building a quick calculation grid from scratch — with the file never leaving the browser, which matters when the spreadsheet is payroll or client data. Outside the scope: statistical work (the STDEV, VAR and PERCENTILE families are stubbed), multi-criteria aggregation with SUMIFS or COUNTIFS, dynamic-array formulas, and any task where formatting is part of the deliverable. Knowing which side of that line your task falls on is the difference between this tool saving you ten minutes and costing you ten.
Questions people ask
Why does =STDEV(A1:A10) show #NAME? when SUM works fine?
The STDEV family is registered but left as an empty function stub in fast-formula-parser 1.0.19, the library this tool builds on — calling one throws a not-implemented error, which renders as #NAME?. MAX, MIN and nine other common stubs used to fail the same way; those now have our own implementations. The formula text is preserved either way and exports intact, so anything we do not compute still computes correctly once the file is opened in Excel or LibreOffice.
Can I open a confidential workbook here without it leaving my machine?
Yes. Parsing, formula evaluation, and export all run in your browser tab. The file is read with the browser's FileReader API and never transmitted.
Why do my dates display as numbers like 46243?
That is the raw Excel date serial (days since December 30, 1899); 46243 is August 9, 2026. The tool has no number-formatting layer, so it shows the underlying value. Date arithmetic on the serials still works correctly.
Will my formulas still recalculate after I export to .xlsx?
Yes. Formulas are written into the file as real formulas, each with its current computed value cached alongside, so Excel shows correct numbers immediately and recalculates normally as you edit.