Skip to main content

What xl3 preserves — and what it doesn't

One question decides whether xl3 fits your template: does the workbook come out the other side intact? This page answers it feature by feature.

Everything below is a spec decision, not a quirk of one implementation. The rows marked deferred are deferred on purpose, with an ADR saying so — that is different from "nobody checked."

The three verdicts

VerdictWhat it means
PreservedComes out identical to the template. Every conforming implementation must do this.
Preserved, range not extendedThe feature survives, but its range keeps the coordinates the template gave it. If @repeat grows the sheet from 1 row to 50, the range still covers the 1 row. There is an author-side fix for each — see the notes.
DeferredXTL 0.1 asserts nothing. It may survive, it may be dropped, and two conforming implementations may disagree. Don't build on it before 1.1.

Layout and formatting

FeatureVerdictNotes
Cell style — font, fill, border, alignmentPreserved
Number and date formatsPreservedAlso inherited by every @repeat-expanded row, not just the first — see Formatting inside a repeat block.
Row height, column widthPreserved
Merged cellsPreservedIn the template and in source data rows (ADR-0033, ADR-0035).
Images and their anchorsPreservedThe anchor does not shift when @repeat inserts rows above it. Put images in a header, footer, or sidebar — outside the block.
Freeze panes and splitsPreserved
Cell comments (notes)PreservedA comment on a {{ [Column] }} cell survives onto the rendered cell.
Sheet protection, per-cell lock/hidden flagsPreservedEngine-written cells inherit the template cell's lock state rather than a synthesized default.
Workbook properties — theme, document propertiesPreservedFixture 120-workbook-properties-preserved.
Native Excel formulas in static cellsPreservedA formula xl3 didn't generate is carried through untouched (ADR-0046). Use this for anything Excel can compute at open time — see Cookbook 16.

Rules with ranges

These survive, but their ranges stay where the template put them. The engine does not silently widen a rule to cover rows it never mentioned — a conditional-format rule that auto-extended could bleed into a footer row the author meant to exclude.

FeatureVerdictAuthor-side fix
Conditional formattingPreserved, range not extendedAnchor the rule to a whole column ($A:$A) in the template. Measured today: a 1-row block that expands to 3 rows leaves the rule on A2:A2.
Data validation (dropdowns, constraints)Preserved, range not extendedSame fix. Measured today: validation authored on B2 stays on B2 alone after expansion.
Named ranges / defined namesPreserved, range not extendedWorkbook-scope and sheet-scope both survive; references into a repeat-expanded area are not re-pointed.
Print area, print titlesPreserved, range not extendedSet the print area to a full column span ($A:$Z) or repeating header rows ($1:$1).

Deferred to 1.1

Four features are deliberately left implementation-defined for 1.0 (ADR-0076). The reference implementation's current behavior is recorded below so you can plan around it, but none of it is a promise — that is what deferred means.

FeatureReference implementation today
Pivot tablesNot carried through. Cannot be authored or preserved.
SparklinesNot carried through.
Structured tables (ListObject)The table part survives, but its ref is not extended: a table authored over a @repeat row still covers only the rows it originally spanned. Widen the ref to a whole column in the template.
Page breaksDropped. Despite sitting next to "print area" conceptually, page breaks are not covered by it. Set them host-side after conversion.
ChartsImplementation-defined (ADR-0036 item 3). The reference implementation reads and writes through ExcelJS, whose chart support is incomplete — do not rely on charts surviving.

Removed on purpose

FeatureBehavior
Macros (VBA, XLM)Stripped, always. An .xlsm input is accepted as XLSX-with-macros-removed and a warning is emitted. Macros never execute, and no host or template can opt back in during 1.x. See SECURITY.md.

Formatting inside a repeat block

Ranges and per-cell formatting behave in opposite directions, and the difference catches people out.

A range anchored at A2:A2 in the template is still A2:A2 after ten rows are emitted. But all ten of those rows carry A2's number format and cell style — engine-written cells take the template cell's formatting, every row, not only the first.

That second half matters more than it sounds. Losing the format on rows 2..N is silent data loss: a column whose first row reads 1,234.50 and whose remainder reads 1234.5 looks like a styling slip and is indistinguishable from one. Nine fixtures tagged data-loss exist to keep that from regressing.

Size limits

LimitValue
Memory~2.2 KB per output cell at scale, on a ~130 MB floor. 2M cells needs roughly 4.2 GB peak RSS.
Cells per conversion~2,000,000 verified end to end; ~500,000 fits in a 2 GB host. Size the host at 2.2 KB/cell before assuming a bigger job runs.
Cell string lengthxl3 writes what you give it. Excel rejects cells over 32,767 characters on read, and xl3 does not pre-validate — check length upstream if the output must open in Excel (ADR-0032).

Exceeding available memory surfaces as a host-level out-of-memory, not as an xl3 error — xl3 cannot catch it. Long conversions accept an AbortSignal.

How much of this is machine-checked

Partly, and it is worth knowing which parts.

  • 123-feature-preservation checks named ranges and cell comments surviving the render cycle, as a Stage 1 cell-value comparison.
  • 170-data-loss-numfmt-preserved-across-expansion checks, in Stage 2, that number formats survive @repeat expansion — the trap described above.
  • A full Stage 2 canonical comparison of every row in the tables above is still pending canonicalizer work under ADR-0006.

So the rows without a fixture behind them are spec promises rather than machine-verified ones. Every implementation runs the same conformance corpus, so what is checked is checked everywhere — but the corpus does not yet cover the whole matrix, and this page would rather say so than imply otherwise.

Where these decisions live

This page is a plain-language reading of the normative sources. When they disagree, they win: