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 four verdicts

VerdictWhat it means
PreservedComes out identical to the template. Every conforming implementation must do this.
Preserved and extendedThe rule survives and a range fully contained in an @repeat block grows with the emitted rows. Partial overlaps are left unchanged so the engine never guesses beyond the authored block.
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

Range-bearing features follow one of two contracts. Conditional formatting and data validation grow only when their authored range is fully contained in an @repeat block. A partial overlap is left untouched, so a rule cannot bleed into a footer the author meant to exclude. Named ranges and print settings are preserved without coordinate changes.

FeatureVerdictAuthor-side fix
Conditional formattingPreserved and extendedA rule on A2:A2 inside a 1-row block expands to A2:A4 when the block emits 3 rows. Whole-column ranges already cover the output and remain unchanged; partial overlaps are preserved as authored.
Data validation (dropdowns, constraints)Preserved and extendedValidation on a cell inside the repeated template rows is copied to the corresponding emitted rows. Validation outside the block is untouched.
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

Range rules and per-cell formatting grow under different contracts, and the difference is worth knowing. Conditional formatting and data validation fully contained in the repeated block extend with it; named ranges and print settings do not. Independently, all emitted rows carry the template cell's number format and style — not only the first row.

That last guarantee 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.
  • 171-cf-dv-range-extension checks, in Stage 2, that contained conditional-formatting and data-validation ranges extend while partial overlaps remain unchanged.
  • 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: