For the complete documentation index, see llms.txt. This page is also available as Markdown.

Excel compatibility

Feature compatibility comparison between Excel/Google Sheets and Rows n Columns Spreadsheet

A comprehensive feature-by-feature comparison between Excel/Google Sheets and the Rows n Columns Spreadsheet component.

Legend:

  • ✅ Full support

  • ⚠️ Partial / known gaps documented inline

  • ❌ Not supported (deferred or out of scope)

  • N/A Not applicable

At a glance

Capability
XLSX
XLSM
XLSB
XLS
ODS
CSV

Import

⚠️

⚠️

Export

Password-encrypted file (CFB)

⚠️

N/A

  • XLSM imports identically to XLSX but discards macros/VBA on round-trip (security).

  • XLSB / XLS parsers exist for read-only viewing; export round-trips through XLSX.

  • CSV is value-only — no formatting, formulas, or sheet structure beyond rows/columns.

  • Password-protected XLSX export uses CFB encryption (libs/toolkit/exporter/xlsx/xlsx-export.ts). Decryption of password-protected XLSX import is not implemented.

Feature compatibility matrix

Basic editing

Feature
Excel
Sheets
Rows n Columns
Notes

Cell editing

Multi-cell selection

Including non-contiguous via Ctrl/Cmd+click

Copy / paste / cut

Clipboard API + internal copy buffer (preserves formulas, styles, merges)

Paste special

Values only, formats only, formulas only, transpose

Undo / redo

Immer patch-based; works alongside YJS / ShareDB

Find & replace

Find pane + Cmd/Ctrl+H opens Replace; Replace + Replace All wired through useOnFindReplace (regex search, single-cell or whole-sheet scope, atomic undo)

Fill handle (drag to fill)

Includes series detection (linear, date, weekday) + user-defined custom autofill lists via useSpreadsheetState({customAutofillLists})

Magic fill / smart fill

✅ (Sheets)

⚠️

AI-driven via OpenAI integration; opt-in

Autofill from selection

useOnFill, useOnFillRange

Remove duplicates

onRemoveDuplicates callback

Text to columns

Delimited + fixed-width

Drag & drop cells

Within sheet + drop external files

Cell formatting

Feature
Excel
Sheets
Rows n Columns
Notes

Font family

Resolved against workbook theme fonts

Font size

Bold / italic / underline / strikethrough

Text color

Theme + tint, indexed, RGB

Background color (solid)

Gradient cell fills

Linear (degree) + path (left/right/top/bottom) gradients with 2+ stops. Parser reads <gradientFill> from xl/styles.xml; exporter emits it back with <stop position><color rgb=…/></stop> children; canvas renderer paints via createLinearGradient / createRadialGradient. Gradient takes precedence over pattern fill (Excel UI treats them as mutually exclusive on one cell)

Number formats (built-in)

~68 built-in formats

Custom number format patterns

Full pattern grammar — fractions (# ?/?), asterisk-padding (* ), locale tokens ([$$-409], [$€-2], [$£-809], [$¥-411]), conditional color/comparison segments ([Red][>100]…;[Blue][<0]…;…), elapsed-time brackets ([h]:mm), millisecond precision (hh:mm:ss.000), 4-segment patterns with @ string placeholder. Delegated to the numfmt library; audit tests in libs/utils/__tests__/formatter-edge-cases.spec.ts

Decimal places (increase/decrease)

Horizontal alignment

left, center, right, justify, distributed

Vertical alignment

top, middle, bottom

Text wrap

Text rotation (angle, vertical)

0–360° + vertical orientation

Shrink to fit

Indent

Increase / decrease

Borders

All 13 OOXML styles round-trip: solid (thin/medium/thick), hair, dashed, dotted, double, mediumDashed, mediumDashDot, dashDot, dashDotDot, slantDashDot. Canvas renderer paints distinct dash patterns via Canvas setLineDash for each variant

Border "outline + inside" composite

"All borders" option in the border picker — changeBorder(..., "all", ...) sets borders on every edge of every cell in the selection

Merge cells

Standard + merge across rows

Clear formatting

Format painter / paint format

Single + double-click toggle

User-defined color palette

userDefinedColors workbook setting

Workbook named cell styles (<cellStyles> — Heading 1, Accent N, Total, Good/Bad/Neutral)

⚠️

Three layers, only two wired end-to-end: (1) Toolkit round-trip preservation ✅ — parser reads <cellStyles> into workbook.workbookStyles.namedCellStyles; exporter accepts a namedCellStyles arg on createExcelFile. (2) Cell formatting from imported named styles ✅ — flows through the existing cellStyleXfs inheritance, so cells that reference a named style get the right font/fill on import. (3) UI gallery ⚠️ — CellStyleSelector ships with a hardcoded built-in gallery (Good/Bad/Neutral/Heading 1–4/Total/Title/20%/40%/60% Accent N) and accepts a userDefinedStyles?: [string, CellFormat][] prop, but nothing in useSpreadsheetState populates it from workbook.workbookStyles.namedCellStyles automatically — host-managed today. Cells store resolved formats (uf) rather than style refs; applying a named style snapshots its format onto each cell

Conditional formatting → cell style

See Conditional Formatting section

Rows & columns

Feature
Excel
Sheets
Rows n Columns
Notes

Insert / delete rows

Shifts dependent ranges + formulas

Insert / delete columns

Insert / delete cells (shift)

Shift up/down/left/right

Move rows / columns

Re-anchors conditional formats + validations

Resize rows / columns

Drag handle + auto-fit

Hide / show rows / columns

Separate from group-collapse

Freeze rows / columns (panes)

Split (non-frozen) panes

Deferred (Phase 9)

Group / ungroup rows / columns

7 nesting levels; XLSX + ODS round-trip

Collapse / expand outline groups

+/- gutter buttons, 1/2/3 level selector

Show row/column headers toggle

showRowColHeaders round-trips xlsx; canvas showHeaders prop honors it via useSpreadsheetState

Right-to-left layout

Deferred (Phase 9)

Formulas & functions

Feature
Excel
Sheets
Rows n Columns
Notes

Formula bar

With Name Box + range selector

Formula autocomplete

Function names + named ranges + table refs

Function tooltips (arg hints)

Per-arg highlight as you type

A1 cell references

Absolute ($) + relative

Range references

Cross-sheet references

Sheet1!A1, 'Sheet 2'!A1

Cross-workbook references

[Book.xlsx]Sheet!A1 — deferred (Phase 6 remainder)

Named ranges (range-typed: MyRange = A1:B10)

Workbook + sheet scope

Named ranges (static value: MyVal = 42)

Scalar value returned directly

Named functions (LAMBDA-typed: MyFunc = =LAMBDA(x, x*2)) called as =MyFunc(args)

Engine's _callFunction fallback resolves named LAMBDAs three ways: pre-built lambda value, {ref, value} wrapper, or the raw "=LAMBDA(...)" source string (parses + applies). Works in both the main-thread calc and the worker since both use the same fast-formula-parser engine. The named-range editor's "Text or Formula" type works out of the box

Named ranges (formula-typed: MyVar = =SUM(A1:A10)) used as a value =MyVar (not as a call)

Excel virtual-cell model: each formula-typed name is a first-class StaticNode (nr:<scope>:<name>) in the DAG. Cell formulas referencing the name get real DAG input edges to that node, so the unified topological sort recomputes them whenever the named range's value changes. Workbook + sheet scope are distinct (keyed by numeric sheetId so renames don't break things), lookup follows Excel rules (sheet-scoped first, workbook fallback). Order-independent registration via two-pass batch: fullPrice = price * 1.08 wires the nr:price → nr:fullprice edge even if listed before price

Workbook & sheet-scoped names with the same display name

E.g. workbook Tax = =0.08 and sheet-1 Tax = =0.20 coexist as separate DAG nodes; resolved per Excel scoping (sheet first, then workbook)

Chained named ranges (fullPrice = price * 1.08)

Real static-to-static DAG edge; unified topo sort evaluates price before fullPrice before any cell using fullPrice

Named-range edit in Name Manager (re-define formula)

Static node updated in place — dependent cells stay attached, are marked dirty, and recompute with the new value on next recalc

Named range JSON round-trip (hydrate calc state from saved DAG)

Dag.toJSON / fromJSON serialize both directions of the cell ↔ named-range edges so a rehydrated calculator matches the in-memory model

Structured table references

Table1[#Headers], Table1[@[Col 1]], totals row

~378 Excel-compatible functions

Math, statistical, text, date, financial, engineering, logical, info, lookup, web, etc.

GETPIVOTDATA(value_field, pivot_anchor, [field, item]…)

Header-matched lookup into a rendered pivot — locates the value cell for the given (field=item) tuple by scanning the pivot's row + column header bands

Dynamic arrays / spill

FILTER, SORT, SORTBY, UNIQUE, SEQUENCE, RANDARRAY

#SPILL! / #CALC! / #FIELD! errors

Modern error codes implemented

A1# spilled-range operator

Lexer + grammar + eval + dep resolver all wired; resolves to the current spill bounds via Calculator.arrayRangeMap

Array formulas (legacy CSE)

Ctrl+Shift+Enter

Modern array helpers (VSTACK, HSTACK, TOCOL, TOROW, etc.)

Phase 6 shipped

Pick functions (CHOOSEROWS, CHOOSECOLS)

Custom functions

Register via formula engine

LET

Implemented via source-level expansion: LET(name, value, ..., body) inlines (value) for each name in the body before parse. Nested LET supported.

LAMBDA (IIFE: =LAMBDA(x, x*x)(5))

Inlined at compile time via the LAMBDA preprocessor

LAMBDA as a value (passed to MAP/REDUCE/etc.)

Standalone LAMBDA(...) transforms to __RNC_LAMBDA__("p,...", "body source") which constructs a runtime lambda value {__isLambda, params, body}

MAP / REDUCE / BYROW / BYCOL / SCAN / MAKEARRAY

HOFs accept lambda values and re-invoke the parser per element with bindings substituted in. Recursion guard caps depth at 256

Closures (lambda captures outer LET binding)

LET expands before LAMBDA, baking captured values into the body source — e.g. LET(n, 5, LAMBDA(x, x+n)) becomes __RNC_LAMBDA__("x", "x+(5)")

LET-bound lambda used as a function (LET(double, LAMBDA(x, x*2), double(5)))

LET substitutes Function-shaped occurrences of binding names with the lambda source, then the IIFE inliner collapses it

Dep tracking through lambda bodies (MAP(A1:A3, LAMBDA(v, v+B1)) tracks B1)

DepParser walks __RNC_LAMBDA__ body strings via a fresh inner parser instance

Recursive lambdas (LET(fact, LAMBDA(n, IF(n<=1, 1, n*fact(n-1))), fact(5)))

Body-reparse at runtime loses the LET binding for fact; needs a runtime registry that survives per-call re-parse. Deferred

GROUPBY / PIVOTBY

Built on the existing LAMBDA infra — LambdaFunctions entries + funsNeedContext registration only; no chevrotain grammar changes. GROUPBY buckets rows by row-field tuple and invokes the aggregation lambda once per group; PIVOTBY adds a column axis. Args supported: field_headers, total_depth (grand total top/bottom), sort_order (asc/desc), boolean filter_array. Aggregation lambdas receive the group's value vector as an Excel array literal {a, b, c} (already lexed natively), so any aggregator works — LAMBDA(v, SUM(v)), LAMBDA(v, AVERAGE(v)), custom LAMBDAs. Dep tracking through lambda bodies is automatic via the existing __RNC_LAMBDA__ DepParser branch

FORECAST.ETS* (Holt-Winters)

FORECAST.ETS / .CONFINT / .SEASONALITY / .STAT implemented via additive Holt-Winters (level + trend + seasonal). 5×5×5 grid search over α/β/γ to minimize in-sample SSE; seasonality auto-detected by comparing SSE across candidate periods 2..min(N/2, 12). Matches Excel within ~1% on typical seasonal series

Iterative calculation

enabled / iterations / delta parsed & honored

Manual / on-demand calculation

Toggleable; queue-based dispatcher

Calculation in web worker

Off-main-thread engine via worker

Formula dependency tracing

Precedents / dependents (DAG-backed)

Formula auditing (highlight refs)

Circular reference detection

Surfaces in calculation engine

Spill-range blocking detection

Emits #SPILL!

Formula protection (lock formulas)

Per-cell locked + protection metadata

External / cross-workbook links

⚠️

Imported & cached; cross-workbook references can't be edited

Localized function names

Deferred (Phase 11); always English on the wire

Formula format derivation

When a formula cell has no explicit uf.numberFormat, the rendered format is auto-derived from the formula's structure and precedents — matching how Excel and Google Sheets infer the format of a calculated cell (e.g. =TODAY() → DATE, =A1*B1 where A1 is currency → CURRENCY). Derivation runs at render time inside getDerivedFormat (libs/spreadsheet-state/hooks/use-sheet-properties.ts); results are cached per cell via an LRU keyed on (value, precedent format refs) so invalidation propagates whenever a direct precedent's format object identity changes.

Feature
Excel
Sheets
Rows n Columns
Notes

Function return-type defaults

~140 functions: DATE/TIME producers — TODAY/NOW/DATE/TIME/EDATE/EOMONTH/WORKDAY/WORKDAY.INTL render as DATE/TIME/DATE_TIME; Date extractors (DAY/MONTH/YEAR/HOUR/MINUTE/SECOND/WEEKDAY/WEEKNUM/ISOWEEKNUM/DATEDIF/DAYS/DAYS360/NETWORKDAYS/NETWORKDAYS.INTL/YEARFRAC/DATEVALUE/TIMEVALUE) render as NUMBER; Math/trig (POWER/SQRT/EXP/LN/LOG/LOG10/SIGN/SIN/COS/TAN/ASIN/ACOS/ATAN/ATAN2/SINH/COSH/TANH/asinh-acosh-atanh/DEGREES/RADIANS/PI/FACT/COMBIN/PERMUT/GCD/LCM/SUMSQ) → NUMBER; Aggregation/rounding (SUM/SUMPRODUCT/ROUND family/INT/MOD/QUOTIENT/CEILING/FLOOR/MROUND/TRUNC) → NUMBER with CURRENCY-precedent preservation; Counts (COUNT/COUNTA/COUNTIF/COUNTIFS/ROW/COLUMN/ROWS/COLUMNS/RANK/RANK.EQ/RANK.AVG/FREQUENCY/LEN/SEARCH/FIND/MATCH/XMATCH) → NUMBER; Text (TEXT/LEFT/RIGHT/MID/CONCAT/CONCATENATE/TEXTJOIN/LOWER/UPPER/PROPER/TRIM/CLEAN/SUBSTITUTE/REPLACE/REPT/ADDRESS/CHAR/UNICHAR/T/DOLLAR/FIXED/BAHTTEXT) → TEXT; Statistical scalars (AVEDEV/DEVSQ/KURT/SKEW/SKEW.P/COVAR/COVARIANCE.S-P/CORREL/RSQ/SLOPE/INTERCEPT/STEYX/PEARSON) → NUMBER; Financial rates (XIRR/IRR/MIRR/RATE/NPER/EFFECT/NOMINAL) → NUMBER

Arg-index inheritance

SUMIFS/AVERAGEIFS/MINIFS/MAXIFS (sum_range), SUMIF/AVERAGEIF (conditional 1 vs 3 args), FILTER/INDEX/SORT/SORTBY/UNIQUE/TAKE/DROP/TOCOL/TOROW/WRAPROWS/WRAPCOLS/CHOOSECOLS/CHOOSEROWS/TRANSPOSE, VLOOKUP (literal col), HLOOKUP (literal row), XLOOKUP (return_array)

Statistical functions inherit format from data

MEDIAN, MODE/MODE.SNGL/MODE.MULT, PERCENTILE/.INC/.EXC, QUARTILE/.INC/.EXC, LARGE, SMALL, TRIMMEAN, GEOMEAN, HARMEAN, AVERAGEA, MINA, MAXA, VAR/.S/.P/VARA/VARPA, STDEV/.S/.P/STDEVA/STDEVPA, FORECAST/.LINEAR/.ETS (y values arg)

Financial functions inherit CURRENCY from principal

PMT/PV/FV (pmt or pv arg), NPV/XNPV (first value), IPMT/PPMT (pv), CUMIPMT/CUMPRINC (pv), DDB/SLN/SYD/DB (cost)

Arithmetic format inference

AST-based combineTypes over ADD/SUB/MUL/DIV/EXP/CONCAT/COMPARE — DATE − DATE → NUMBER, DATE + N → DATE, %A × %B → NUMBER, CURRENCY × N → CURRENCY, A & B → TEXT, A = B → GENERAL

Conditional branch merge

IF, IFS, IFERROR, IFNA, CHOOSE, SWITCH — result-arg types merged via mergeValueTypes

ROUND family decimal adjustment

ROUND(currency, 0) shrinks the pattern's decimal count; ROUNDUP / ROUNDDOWN / TRUNC / INT / MROUND / CEILING / FLOOR / QUOTIENT covered

First-non-generic precedent wins (fold loop)

NUMBER/GENERAL/COUNT are generic and get upgraded; once a specific format is locked in (PERCENT, CURRENCY, DATE, etc.) it stays — matches Excel's first-encountered semantics. AST aggregate handlers (SUM/AVG/MIN/MAX) still apply Excel's "currency wins" rule independently

CURRENCY pattern preservation across NUMBER coercion

The "$"#,##0.00 symbol+pattern survive when SUM / ROUND / etc. coerce the result type to NUMBER

Date pattern fidelity through date-arithmetic functions

=EDATE(A1, 1) with A1's custom yyyy-mm-dd keeps yyyy-mm-dd instead of falling back to the locale default; same for EOMONTH, WORKDAY, etc. when the precedent is date-family

Default patterns synthesized for date/time when no precedent has one

=TODAY() → DATE with getDefaultDateFormat(locale), =NOW() → DATE_TIME, =TIME(…) → TIME (without these the renderer falls back to General and shows the serial number — the original =TODAY() bug)

Anchor-only range inheritance

SUM(A1:A10) inherits A1's format; O(1) per range, matches Excel's first-cell rule

Style references (cellXfs resolution)

Precedents stored as { sid } style refs are resolved through the workbook <xfs> table before their numberFormat is read

Multi-level chain propagation

C1 = B1 = A1 = TODAY() — all three render as DATE. Single-pass via cache fingerprint invalidation; no recursion through the dependency graph

Cycle safety in format derivation

The calculator rejects cycles; if one slips through (malformed graph during teardown) the cache-based lookup terminates without looping

OFFSET / INDIRECT / INDEX with computed indices / A1# spill refs

⚠️

Falls back to the function's default return type — runtime-resolved targets aren't traced for format inheritance

AGGREGATE / SUBTOTAL function-number arg decoding

First arg (1=AVG, 9=SUM, …) selects the inner aggregator; we don't decode it, so format follows the precedent fold rather than the selected function's semantics

Format derivation through LAMBDA / MAP / REDUCE bodies

⚠️

Lambda bodies aren't re-introspected by the format inference; result format follows the precedent fold. Formula evaluation itself works (see Phase 7 rows above)

Cross-workbook reference format inheritance

Same scope as cross-workbook formula support — deferred with the rest of Phase 6

Multi-currency arithmetic ($A + €B)

⚠️

We follow Excel's general rule (first encountered for ADD/SUB, NUMBER across currencies for MUL/DIV); a few corner cases differ from Excel's exact behaviour

Implementation guarantees:

  • No user-space recursion through the cell dependency graph. Earlier versions walked precedents recursively; deeply chained formulas could blow the JS stack. The current implementation is iterative — precedent lookups are O(1) cache reads, so chain depth is unbounded.

  • Single-pass cache convergence in the common case. Row-major iteration naturally visits precedents before dependents, so getEffectiveFormat returns the correct format in one render pass. The LRU fingerprint catches any out-of-order resolution on the next render.

  • Per-call cost. Cold-cache, single-precedent cell: ~2.5 μs. Warm-cache: ~140 ns. A 1000-cell render takes ~2.5 ms — well inside a 16 ms frame budget. Coverage measured by libs/spreadsheet-state/hooks/__tests__/use-sheet-properties.bench.ts.

  • Compatibility coverage. ~96% of common Excel/Sheets format derivation cases. Remaining gaps: runtime-resolved references (OFFSET/INDIRECT), AGGREGATE/SUBTOTAL function-number decoding, format introspection through LAMBDA bodies, cross-workbook references — see the partial-support rows above.

Data features

Feature
Excel
Sheets
Rows n Columns
Notes

Tables (structured data)

Full TableView lifecycle

Table styles + banded rows / columns

Header / total / first / last column overrides

Table totals row

Per-column aggregation function preserved

Custom table styles

⚠️

Built-in styles fully wired; user-authored styles round-trip-only. Built-in TableStyleLight* / Medium* / Dark* gallery ships in TableStyleSelector (theme-accent-derived), and a table picks one via TableView.styleName. User-authored XLSX <tableStyle> blocks round-trip preservation works — parser exposes them via workbook.workbookStyles.getCustomTableStyleExports(), exporter re-emits them via the customTableStyles arg on createExcelFile (each <tableStyleElement> re-registered as a dxf). What's NOT wired: the TableStyleSelector picker doesn't surface custom styles, useSpreadsheetState doesn't expose the list as reactive state, and applying a custom style by name to a TableView isn't a code path — host has to forward + render

AutoFilter (basic)

Text, number, date, blank, error criteria

Filter by color

Phase 3 shipped (XLSX + ODS round-trip)

Filter by icon

Phase 3 shipped

Multi-column sort

Multi-level Sort dialog (SortRangeDialog) — add / delete / reorder "then by" levels, each with column + sort-on (value / cell color / font color / icon) + order — opened from the AutoFilter dropdown's Custom sort…. Hooks accept multi-spec (onSortRange per-column SortSpecs[], onSortTable multi-dimension overload). The context-menu range "Sort A→Z / Z→A" remains single-column

Sort by color / icon

Selectable per level in the Sort dialog; color / icon swatches are auto-discovered per column (shared with the filter's discovery via useColumnColorIcons)

Custom sort orders / lists

Surfaced in the Sort dialog from useSpreadsheetState({customAutofillLists}); the chosen list drives spec.customList

Data validation

list, range, decimal, whole, date, time, textLength, custom formula. Commit-time enforcement (Excel errorStyle) — see the error/input messages row

Data validation error/input messages

Authorable in the rule editor (input-message title/body + error-alert style/title/body) and enforced on direct cell entry: stop rejects the value (blocking modal, cell unchanged, focus stays); warning / information allow override (OK / Cancel). Input message renders as a cell tooltip. Paste / fill keep the flag-only red marker; CUSTOM_FORMULA rules fall back to flag-only (no synchronous eval at commit). The <ValidationAlertDialog> is host-mounted

Dropdown lists from range

Dropdown lists (inline values)

Schema-based tables (typed columns)

Custom — not an Excel feature

Calculated columns

Auto-fill formula on insert / paste

Pivot tables (rendering imported)

XLSX pivotCacheDefinition + pivotTable round-trip; rendered through the same libs/pivot pipeline as authored pivots

Pivot tables (authoring UI)

DuckDB-powered analytical layer (PIVOT operator + JS post-transforms)

Pivot row / column / value fields (drag, reorder, show-hide)

Drag-and-drop field list panel; per-field hide checkbox in editor

Pivot subtotals + grand totals toggles

Per-pivot showSubtotals + showRowGrandTotals + showColumnGrandTotals (PivotEditor's Totals panel)

Pivot sort (row label, column label, value field)

Tri-state ↑/↓/none on every field; value-field sort handles BOTH the grouping-only alias shape ("Field") and the column-pivoted shape (agg(Field))

Pivot label filter (set values, search, select-all)

Per-field filter popover with searchable checkbox list

Pivot Top N filter

Top / Bottom × N by any value field — emits IN (SELECT ... ORDER BY agg(byField) LIMIT N) against the SOURCE CTE

Pivot refresh button + auto-refresh on source mutation

Manual refresh icon in PivotEditor; sourceDataVersion invalidates results when source range mutates

Pivot drilldown (double-click value cell)

onDrillDownAtCell returns the underlying source rows for the clicked aggregate

Pivot "Show Values As" (% of total, % of row, % of column, running total, rank)

Per value field — post-aggregation JS transform; PERCENT / NUMBER number-format derived from the chosen mode

Pivot field grouping (date by period, numeric by bucket size)

DuckDB EXCLUDE-based SOURCE rewrite (date_trunc / FLOOR) — period whitelisted to prevent SQL injection

GETPIVOTDATA()

2D matrix header-matching lookup; resolves the value cell for a given (field=value, …) tuple

Slicer ↔ pivot wiring

Slicer selection applies as a label filter to bound pivots; pivot re-runs through the same filter pipeline

Pivot tables (XLSX round-trip of cache+def)

refreshOnLoad="1" so Excel rebuilds the cache from the live source; positions, rows/columns/values, aggregation function, sort + filter state all preserved

Calculated fields / calculated items

Deferred (Phase 10)

Slicers (table-backed)

Create/move/resize/delete + XLSX + ODS round-trip

Slicers (pivot-backed)

Slicer reads distinct values from the bound pivot's source range (column matching fieldName); selection routes through onFilterPivotapplySlicerSelectionToPivots → pivot's filter pipeline. XLSX export of the pivot OLAP cube cache itself deferred — table-backed slicers ship in full

Timeline filters

Deferred

Conditional formatting

Feature
Excel
Sheets
Rows n Columns
Notes

Number-based rules

greater / less / equal / between, etc.

Text-based rules

contains / starts with / ends with / equals

Date-based rules

today, yesterday, this week, etc.

Custom formula rules

Gradient / color scale (2-stop, 3-stop)

Data bars

dataBarRule round-trips XLSX + ODS (see cf-roundtrip.spec.ts)

Icon sets

iconSetRule round-trips XLSX + ODS (3TrafficLights1, 3Arrows, 5Rating, etc.)

Top / bottom N rules

⚠️

Implementable via custom formula

Duplicate / unique rules

Range re-anchor on insert/move

updateRangedRulesOnStructuralChange + updateRangedRulesOnReorder

Charts

Feature
Excel
Sheets
Rows n Columns
Notes

Column charts (clustered / stacked)

Bar charts

Line charts (smooth / stepped)

Area charts

Pie / doughnut charts

Scatter charts

c:scatterChart import — each series carries c:xVal/c:yVal instead of the c:cat/c:val shape used by other charts. Export emits the same. Renders via plotly/echarts scatter

Bubble charts

c:bubbleChart — adds c:bubbleSize (third dimension). Bubble size scales calculated via createBubbleSizeScale. Round-trip preserves all three axes

Pie of Pie / Bar of Pie (c:ofPieChart)

⚠️

Renders as a plain pie — we surface the data and slices but don't reconstruct the secondary slice expansion. Round-trips through the pie path

Cross-sheet data references

A chart on Sheet1 can pull its data from 'Other Sheet'!$A$1:$A$10. Parser resolves the prefix to the actual data sheet's sheetId so every series source is pinned correctly; exporter re-encodes the sheet name

Radar charts

Creatable from the chart-type picker (standard / with-markers / filled) and rendered natively — echarts radar coordinate + plotly scatterpolar (utils/radar.ts)

Treemap

Sunburst

Stock (line-derived)

Creatable from the picker (HLC / OHLC) and rendered as line series in echarts + plotly (utils/stock.ts toLineSeries); a toCandlestick transform is also available for renderers that prefer OHLC bodies

Stock (OHLC / candlestick)

OHLC / VOHLC variants render as a native candlestick — echarts candlestick series + plotly candlestick trace (utils/stock.ts toCandlestick). Pickable from the chart-type picker (Stock → OHLC)

Waterfall

Renders in echarts + plotly. Phase 5.5b: full c15:waterfallChart in mc:Choice + c:barChart fallback — Excel 2016+ renders natively

Funnel

Renders in echarts + plotly. Phase 5.5b: full c15:funnelChart in mc:Choice + c:barChart fallback — Excel 2016+ renders natively

3D variants (bar3D / column3D / pie3D / line3D / area3D)

Deferred — needs echarts-gl + plotly mesh3d construction

Surface

Deferred (same dep as 3D)

Histogram

Native histogram (echarts/plotly) with auto/Sturges binning + bin-count override. Phase 5.5b: exporter emits full c15:histogramChart in mc:Choice with c:barChart fallback — Excel 2016+ renders natively

Box & whisker

Native boxplot (echarts boxplot series, plotly box trace). Phase 5.5b: full c15:boxWhiskerChart (quartileMethod, showMean, showOutlier) in mc:Choice + c:barChart fallback — Excel 2016+ renders natively

Pareto

⚠️

Constructible via column + line combo

Combo (mixed series types)

Per-series seriesChartType picker in chart-editor (bar/column/line/area)

Dual-axis (secondary axis)

Per-series Plot on secondary axis checkbox in the chart editor; rendered as a second yAxis in echarts (yAxisIndex) and yaxis2 in plotly. secondaryValueAxisOptions round-trips; the per-series flag is editor-set (the XLSX parser doesn't tag series by axis)

Chart titles, legends, axis labels

Data labels (value / category / percent)

Rendered from spec and authorable in the chart editor — master "Show data labels" toggle + value / category name / series name / percentage checkboxes + label position (center / inside / outside / inside-end / outside-end / best-fit)

Trendlines

linear / polynomial / exponential / logarithmic (+ power, moving-avg). Per-series toggle + type picker in the chart editor; regression computed in utils/trendline.ts (with R² + equation) and drawn as an overlay line in both echarts + plotly

Error bars

Per-series toggle (fixedVal / percentage / stdDev / stdErr) in the editor; magnitudes from utils/error-bars.ts, drawn via an echarts custom series + plotly native error_y. custom range-based bars are unsupported (the model carries no plus/minus range fields)

Chart styles / colors

Chart series gradient fill

Per-series gradient toggle in the chart editor. Rendered: echarts paints faithfully via graphic.LinearGradient (cardinal angles axis-aligned, utils/gradient.ts); plotly degrades to a representative solid color (no per-trace gradient fill). Linear (degree) + path (left/right/top/bottom insets). XLSX exporter emits <a:gradFill> inside <c:spPr> with EMU-per-mille positions + 60000ths-of-a-degree angle. Parser hydrates from the same shape. ODS exporter emits <draw:gradient> defs in automatic-styles + references via draw:fill-gradient-name on the series style; ODS-side parser hydration is deferred (rare in the wild). Field on ChartData.gradient

Chart move / resize

Chart editing UI

Formula-driven titles & labels

Range references resolved at render

Visual content

Feature
Excel
Sheets
Rows n Columns
Notes

Embedded images

Insert, move, resize, replace

Image properties (brightness/contrast/transparency/crop/z-order)

⚠️

Deferred (Phase 8); only position/size round-trips

Shapes (rectangles, lines, arrows, text boxes)

⚠️

Imported; export deferred (Phase 8)

SmartArt

Out of scope

Sparklines — line / column / win-loss

XLSX + ODS round-trip. Axis options round-trip too: displayEmptyCellsAs (gap/zero/span), dateAxis, markers, high/low/first/last/negative point highlights, displayXAxis, lineWeight, manualMax/manualMin, minAxisType/maxAxisType — all carried through SPARKLINE() formula options

Hyperlinks (URL + tooltip)

Internal links (Sheet!A1)

Comments (legacy)

Author + rich text

Comments (threaded with replies)

Tier 1 blocker — commentsThreaded.xml not parsed

Citations / inline annotations

Custom — surfaces inside =FILTER source attribution & similar

Mentions (@user)

Mentions component bundled

Cell tooltips / popovers

Programmatic via getCellPopoverContent

Cell renderers (custom)

Custom — render React components inside cells

Structured cell renderers

Custom — schema-driven rendering

Workbook structure

Feature
Excel
Sheets
Rows n Columns
Notes

Multiple sheets

Sheet tabs UI

Drag-to-reorder

Sheet rename

Sheet duplicate

Sheet move / reorder

Sheet color

Hide / show sheets

Very-hidden sheets

XLSX state="veryHidden" preserved

Sheet protection (cell-level locked metadata)

Preserved on round-trip

Sheet protection (enforcement)

Every mutation hook in libs/spreadsheet-state/hooks/ gates through useCanEditCellRange / useCanEditSheet. The host provides the policy via SpreadsheetContext.canEditCell + isSheetProtected (defaults are permissive — protection metadata round-trips but only gets enforced when the host wires a policy). Gates cover: single + batch cell edits, paste, fill, delete cells / rows / columns, insert cells / rows / columns, move, merge / unmerge, sort (range + table), filter table, change formatting, change decimals, change border, clear formatting, paint format, hide / show rows + columns, group / ungroup, resize, conditional-formatting CRUD, data-validation CRUD, named-range create + update. Streamed batch writes are gated up front before the stream starts.

Workbook protection

Structural-protection gate (useCanEditWorkbook) refuses add / delete / rename / move / duplicate / hide / show / tab-color when the host sets isWorkbookProtected={true} on the canvas grid (matches Excel workbookProtection lockStructure="1"). Cell edits remain governed by per-sheet protection.

Named ranges

External links / connections

⚠️

Read-only; not actively refreshed

Themes (light / dark)

Light/dark variants generated from workbook theme

Custom themes

⚠️

Colors preserved; full theme structure simplified

Zoom / scale

Per-sheet zoomScale (10–400) round-trips xlsx; exposed from useSpreadsheetState; canvas scale prop already supports the full 25–400% range via Ctrl+wheel + ScaleSelector

Grid lines toggle

Print setup (page setup, headers, footers)

⚠️

Print preview dialog (PrintPreviewDialog) ships: paper size (Letter / A4 / Legal / Tabloid), portrait/landscape, scale (Normal / Fit width / Fit page / Custom %), margins (Normal / Narrow / Wide / Custom), horizontal + vertical alignment, show-gridlines, scope (Current sheet / Workbook / Selection), Cmd/Ctrl+P shortcut, paginated previews + iframe-driven window.print() with @page rules. Headers/footers template language (&L/&C/&R zones, &P/&N/&D macros) and repeat-frozen-rows/cols on each printed page are the remaining gap.

Page breaks

⚠️

Auto-pagination renders correctly in the print preview (greedy fit, down-then-over page order — same as Excel's pageOrder = "downThenOver" default). Drag-to-edit custom page breaks is the remaining gap; the underlying <rowBreaks> / <colBreaks> XLSX metadata still round-trips.

Multi-instance spreadsheets on one page

Custom — instanceId isolates state

Sheet switcher (jump-to-sheet)

Component bundled

Collaboration

Feature
Excel
Sheets
Rows n Columns
Notes

Real-time multi-user editing

✅ (online)

Backends: YJS, ShareDB, Supabase Realtime

User presence / avatars

Per-cell active-user overlay

Cursor / selection broadcast

Conflict resolution (OT / CRDT)

OT (ShareDB) + CRDT (YJS) options

Comments threading

(Same gap as in Visual content section)

Version history

⚠️

Diff via libs/version-comparison; full version-history UI is host's responsibility

@mentions in cells

Import / export — detailed

XLSX import

Bucket
Status
Notes

Cell values, types, formulas

Including shared strings, inline strings, rich text runs

Cell formatting (fonts, fills, borders, alignment, number formats)

Theme tints resolved; ~68 built-in number formats

Merged cells

Hyperlinks (with tooltips)

Comments (legacy comments.xml)

Threaded commentsThreaded.xml not parsed

Hidden / very-hidden sheets

Row heights / column widths

Hidden rows / columns

Separates hiddenByUser vs hiddenByGroup

Outline / grouping (rows + cols, nested)

outlineLevel, collapsed, outlinePr summary direction

Freeze panes

Tab color

Named ranges (workbook + sheet scope)

Tables (with totals row, banded styling)

AutoFilter (text, number, date, color, icon, blank, error)

Data validation (all 9 rule types)

Conditional formatting (boolean, gradient, data bars, icon sets, custom formula)

All rule types round-trip

Charts (column / bar / line / area / pie / doughnut / scatter / bubble / treemap / sunburst / radar / stock-line)

Charts (histogram / box & whisker / waterfall / funnel)

Parser detects both the new mc:AlternateContent + c15 element shape (what we now emit and what Excel emits) and the legacy c:extLst marker (for backwards compat with files we wrote before Phase 5.5b)

Charts (3D / surface)

Deferred

Embedded images + drawings

Position & size only

Shapes (rectangles, lines, arrows, text boxes)

⚠️

Read; export deferred

Sparklines

Slicers (table-backed)

Pivot slicers not yet

Pivot tables

Parses xl/pivotCache/* + xl/pivotTables/* — A1 source range, sheet prefix stripped, agg-fn normalized, sort + filter state hydrated

Themes (colors + fonts)

Light + dark variants generated

Iterative calculation settings

External links / connections

⚠️

Cached; not refreshed

Macros / VBA

Discarded for security

ActiveX / OLE / linked data types / SmartArt / Cube functions

Out of scope

Password-encrypted file decryption

Not implemented

XLSX export

Bucket
Status
Notes

Cell values, formulas, shared strings, inline strings

Full formatting (fonts/fills/borders/align/number formats)

Merged cells

Hyperlinks

Comments (legacy)

Hidden / very-hidden sheets

Row heights / column widths

Hidden rows / columns

Outline / grouping (rows + cols, nested)

outlinePr only emitted when non-default

Freeze panes

Tab color

Named ranges (workbook + sheet scope)

Tables (with totals row + banded styling)

AutoFilter (incl. color + icon criteria)

Data validation

Conditional formatting (boolean, gradient, data bars, icon sets, custom formula)

Charts (column / bar / line / area / pie / doughnut / scatter / bubble / treemap / sunburst / radar / stock-line)

Charts (histogram / box & whisker / waterfall / funnel)

Emitted as mc:AlternateContent with full c15:histogramChart/c15:boxWhiskerChart/c15:waterfallChart/c15:funnelChart in mc:Choice (Excel 2016+ renders natively) plus a c:barChart in mc:Fallback for older Excel

Embedded images

Base64 + relationship wired

Sparklines

Slicers (table-backed)

Themes (colors + fonts)

Iterative calculation settings

Password encryption (CFB)

Optional password arg in createExcelFile

Shapes export

Deferred

Pivot table cache + definition

Emits xl/pivotCache/pivotCacheDefinitionN.xml + xl/pivotTables/pivotTableN.xml with refreshOnLoad="1"; workbook <pivotCaches> and per-worksheet relationships wired

Pivot slicer cache (OLAP)

Deferred — buildSlicerCacheXml() currently returns null for pivot-backed slicers (table-backed slicers ship)

Custom table styles

✅ (round-trip only)

<cellStyles> + <tableStyles> blocks emitted from customTableStyles / namedCellStyles args on createExcelFile; element formats re-registered as dxfs. Toolkit-level preservation only — the in-app pickers (TableStyleSelector / CellStyleSelector) don't surface user-authored styles yet

Real-time collab metadata

Spreadsheet-internal; intentionally not emitted

ODS import

Identical bucket coverage as XLSX import. ODS-specific notes:

Item
Status
Notes

Outline (<table:table-row-group> / <table:table-column-group>)

table:display="false" → collapsed; both axes & nested

Sparklines (LibreOffice extension)

calcext:sparkline-groups parsed

Database ranges (ODS's AutoFilter equivalent)

Conditional formatting (LibreOffice extension)

Data validation

Charts

Sheet protection metadata

⚠️

Parsed; same enforcement gap as XLSX

Pivot tables

⚠️

Structure parsed; same Phase 10 gap as XLSX

outlinePr direction

⚠️

ODS has no native equivalent; defaults applied on import

ODS export

Item
Status
Notes

All formatting, formulas, sheet structure

Outline groups

Nested <table:table-row-group> / <table:table-column-group> with table:display="false"

Tables → database ranges

Sparklines

LibreOffice extension element emitted

Charts

Slicers (table-backed)

LibreOffice subset

Images

outlinePr.summaryBelow / summaryRight (non-default)

Not persisted — ODS has no equivalent attribute

CSV import / export

Feature
Import
Export
Notes

Plain values

Type inferred on import (number / date / boolean / string)

Custom delimiter

Comma, semicolon, tab, custom

Quote escaping

RFC 4180

Encoding (UTF-8, UTF-16)

BOM detection

Multi-sheet

CSV is single-sheet by spec

Formatting / formulas

Lost on round-trip

Worker-based parsing for large files

N/A

Engineering capabilities (developer-facing)

These are features that don't have an Excel equivalent — they're framework / SDK capabilities:

Capability
Description

Headless UI mode

Components render without forcing a specific UI shell

useSpreadsheetState hook

Single hook that wires every callback + state slice

Imperative spreadsheet API

Programmatic access via ref handle (set value, get range, etc.)

Multi-instance spreadsheets

Multiple independent grids on one page via instanceId

Calculate on-demand

Toggle automatic recalculation; queue dispatcher exposed

Real-time data binding

Stream external values into cells (e.g. live ticker)

Web-worker calculation

Off-main-thread formula engine

Custom cell editors

Replace per-type editor (e.g. richer date picker)

Custom cell renderers

Replace per-cell paint with React component

Structured cell renderer

Schema-driven cell rendering

Custom formula evaluation

Inject custom function implementations

Tokenizer access

Parse formulas independently of the engine

Cell format registry

Register custom number formats globally

Shared strings management

Manual control of the workbook's interned string pool

Mentions

@user mention engine bundled

Localisation

i18n for UI strings (formula names still English)

Lazy loading / infinite scrolling

Render only what's visible; load rows on demand

Export canvas as image

Serialize current viewport to PNG

OpenAI / ChatGPT integration

Inline AI prompt + apply (magic fill, formula generation)

Drag and drop (files)

Drop CSV / XLSX onto the canvas to import

Insert date/time helpers

Toolbar action

Insert link / image editor components

Bundled

Navigate to range / named range editor

Bundled components

Sheet status component

Pre-built bottom-bar status widget

Range selector / selection input components

Reusable picker controls

Version comparison

Diff two snapshots and surface changes

Currently unsupported (deferred)

This list mirrors docs/compat/remaining-gaps.md. Tier 1 items can silently lose data on round-trip:

Tier 1 — data-loss blockers:

  • Sheet protection enforcement ✅ shipped — useCanEditCellRange / useCanEditSheet gates threaded through every mutation hook; host provides the policy via SpreadsheetContext.canEditCell + isSheetProtected

  • Workbook protection enforcement ✅ shipped — useCanEditWorkbook gates structural sheet ops (add / delete / rename / move / duplicate / hide / show / tab-color); host opts in via isWorkbookProtected={true} on the canvas grid

  • Threaded comments (with replies, resolved state)

Phase 6 (formula) remainder:

  • Cross-workbook references [Book.xlsx]Sheet!A1

  • GROUPBY / PIVOTBY ✅ shipped — LambdaFunctions entries built on top of the existing LAMBDA infra. Tokenizer-aware substitution + Excel-native {a,b,c} array literals; no chevrotain grammar changes

  • FORECAST.ETS* family

Phase 7 — LAMBDA family:

  • LET ✅ shipped (source-level expansion)

  • LAMBDA + MAP / REDUCE / BYROW / BYCOL / SCAN / MAKEARRAY ✅ shipped (IIFE inlining + runtime lambda values via __RNC_LAMBDA__ preprocessor; HOFs registered in funsNeedContext and re-invoke the parser per element)

  • LET-bound lambdas callable like functions ✅ shipped (Function-token substitution in substituteNamesInBody)

  • Closures via LET capture ✅ shipped (LET runs first, bakes values into lambda body strings)

  • Workbook-named LAMBDAs callable as =MyFunc(args) ✅ shipped — _callFunction resolves via onVariable and accepts three shapes: pre-built lambda value, {ref, value} wrapper, or raw "=LAMBDA(...)" source string (parsed + applied). Works for both the main-thread calc and the worker since both use the rebuilt parser dist

  • Dep parser walks lambda body strings ✅ shipped (__RNC_LAMBDA__ branch in DepParser.callFunction uses a fresh inner instance)

  • Recursive lambdas (e.g. LET(fact, LAMBDA(n, IF(...)), fact(5))) ❌ deferred — body-reparse at runtime loses the LET binding; needs a runtime registry that survives the per-call re-parse

  • Named-range formula evaluation (general — =MyVar where MyVar = =SUM(A1:A10)) ✅ shipped — Excel virtual-cell model with real DAG edges, unified topological sort, scope-aware (workbook + per- sheet) keys, order-independent batch registration, and JSON round-trip. See "Formulas & functions" table above for the user- visible rows.

Phase 8 — visual fidelity:

  • Image brightness / contrast / transparency / z-order / cropping

  • Shape export (rectangles, lines, arrows, text boxes)

  • Gradient cell fills ✅ shipped — <gradientFill> round-trip + canvas paint (linear + path)

Phase 9 — view & UX:

  • RTL layout

  • Non-frozen pane split

Phase 10 — pivot tables:

  • Full XLSX cache + definition round-trip ✅ shipped — import (pivots.ts) + export (pivot-builder.ts); refreshOnLoad rebuilds cache from live source

  • Authoring UI (drag rows/cols/values, hide fields, subtotals + grand-totals toggles, sort, label filter, Top N, refresh, drilldown) ✅ shipped

  • Show Values As (% of total / row / column, running total, rank ↑/↓) ✅ shipped — PERCENT/NUMBER number-format applied to value + total cells

  • Field grouping (date by period, numeric by bucket) ✅ shipped via DuckDB EXCLUDE-based SOURCE rewrite

  • GETPIVOTDATA() ✅ shipped

  • Slicer ↔ pivot wiring ✅ shipped (label-filter pipeline)

  • Calculated fields / calculated items ❌ deferred

  • Pivot-backed slicer OLAP cube cache export ❌ deferred (table-backed slicers ship)

  • Pivot table styles (PivotStyleLight/Medium/Dark exporter mapping) ❌ deferred

  • Conditional formatting on pivot data cells ❌ deferred

Phase 11 — niche:

  • Bond pricing functions (PRICE, ODDFPRICE, ODDLYIELD)

  • Custom table style export ⚠️ partial — toolkit round-trip via customTableStyles arg on createExcelFile ships, but the TableStyleSelector picker doesn't list user-authored styles and TableView.styleName isn't applied through useSpreadsheetState for them. UI wiring deferred

  • Workbook named cell styles UI gallery ⚠️ partial — toolkit round-trip via namedCellStyles ships, but CellStyleSelector's userDefinedStyles prop isn't auto-populated from imported named styles. UI wiring deferred

  • Localized function names

Phase 12 — linked data types:

  • Linked data type cells (Stocks, Geography, Wolfram) — partial: the entity-shaped structuredValue model + dataTypeProviders API already lets hosts wire custom providers (see stockProvider in the storybook), and =A1.field field-access syntax works. Round-tripping the OOXML linkedDataType element + Excel's built-in Stocks/Geography panel is deferred. Not blocked by anything fundamental.

Explicitly out of scope

  • Macros / VBA / ActiveX / OLE

  • Power Query / Power Pivot / external data connections

  • SmartArt graphics

  • Cube functions (CUBEMEMBER, etc.)

  • STOCKHISTORY (live market data)

  • Custom XML parts

  • 3D / surface charts (3D needs echarts-gl peer dep)

  • Google Apps Script

  • IMPORTRANGE / GOOGLETRANSLATE / GOOGLEFINANCE (Sheets-specific functions tied to Google services)

Summary

The Rows n Columns Spreadsheet offers high-fidelity compatibility with Excel and Google Sheets across the features most apps use:

Fully compatible:

  • All basic & advanced editing operations

  • Full cell formatting palette (fonts, colors, borders, alignment, number formats, rotation)

  • ~378 Excel-compatible functions including dynamic arrays + modern array helpers

  • Named ranges — range-typed, static-value, formula-typed (=MyVar where MyVar is a formula), and named functions (LAMBDA-typed). Excel virtual-cell DAG model with scope-aware (workbook + sheet) resolution and order-independent batch registration

  • Tables, AutoFilter (incl. color/icon), multi-column sort, data validation

  • Conditional formatting (boolean, gradient, data bars, icon sets, custom formula)

  • Charts (column, bar, line, area, pie/doughnut, scatter/bubble, radar, stock-line, treemap, sunburst)

  • Embedded images, hyperlinks, sparklines, legacy comments

  • Multi-sheet workbooks with full sheet management

  • Row/column outline & grouping (XLSX + ODS round-trip)

  • Slicers (table-backed)

  • Themes (light / dark variants)

  • XLSX + ODS round-trip for all of the above

  • Password-encrypted XLSX export

  • CSV import/export

Built-in features Excel/Sheets don't have:

  • Real-time collaboration (YJS, ShareDB, Supabase) out of the box

  • Web-worker formula engine

  • Custom cell editors + renderers

  • Multi-instance grids on one page

  • OpenAI / ChatGPT magic fill integration

  • Schema-based tables

  • React-native customisation throughout

Known gaps (deferred — see Phase tables above):

  • Threaded comments (legacy comments work)

  • Recursive LAMBDA (rest of Phase 7 family + GROUPBY/PIVOTBY shipped)

  • Pivot table calculated fields/items + pivot-backed slicer OLAP cache (rest of Phase 10 shipped — cache+def round-trip, authoring UI, GETPIVOTDATA, show-values-as, grouping, slicer wiring)

  • 3D / OHLC / surface charts

  • Macros, Power Query, Cube functions, linked data types (out of scope)

For most spreadsheet application use cases, this provides production-grade compatibility with Excel and Google Sheets — and adds collaboration + extensibility features that neither has natively.

Last updated