Changelog
All notable changes to TabularJS. The full history is on GitHub.
2.2.0 (2026-10-29)
Section titled “2.2.0 (2026-10-29)”Lotus 1-2-3 WK3, WK4 and .123 files now return their data, files with the wrong extension open, and a long list of fixes found by testing real files from Excel, LibreOffice and openpyxl.
- Lotus 1-2-3 WK3, WK4 and .123 (1-2-3 97 and Millennium): numbers, labels, formula results, error and N/A cells, notes, sheet names, column widths and hidden columns. Formulas return their saved results, as for WK1
- Wrong extensions: an
.xlsxrenamed.xls, an.xlssaved as.xlsx, or a CSV, HTML or XML 2003 file saved as.xlsis parsed as what it really is.options.extensionis never overridden - Password-protected .xlsx files report
Workbook is encrypted with a password - SYLK: the
encodingoption
- XLSX: shared formulas reached only the first cell of a filled range; every cell now gets its own formula. Inline strings (written by openpyxl and many generators) came back empty,
0came back as"0", error cells read##VALUE!, and shared strings kept&and phonetic readings - DIF: the last row was duplicated, Excel files came back with as many columns as rows (52 x 52 instead of 52 x 17), and TRUE/FALSE, NA and ERROR came back as numbers
- SYLK: text with a semicolon (
;;) was cut off - HTML: only the first
<tbody>was read,<th>cells were moved in front of<td>cells, and entities were not decoded in Node.js - ODS: comment text leaked into cell values, references to other sheets kept OpenDocument syntax (
[$Rates.A1]now readsRates!A1), and the lines of a multi-line cell were glued together - CSV: a byte order mark stayed in the first cell when the file was passed as a path; UTF-16 files with a byte order mark are decoded correctly
- Node.js:
Fileobjects (fromFormDatain Next.js and other frameworks) failed for .xlsx and .ods - 540 automated tests (up from 433)
2.1.1 (2026-10-28)
Section titled “2.1.1 (2026-10-28)”Fixes found by running the LibreOffice import test corpus against TabularJS.
- dBase (.dbf): text fields are decoded with the codepage the file declares (Cyrillic, Central European, CJK…), or with
options.encodingwhen given; before, they came out garbled - dBase (.dbf): a crafted file could declare millions of records it does not contain and exhaust memory; records are now limited to what the file holds
- SYLK: files with CR-only line endings (classic Mac Excel) parsed as empty
- Lotus 1-2-3: formula cells exposed raw token bytes as
cells[address].formula; only the calculated value is returned now. String results of formulas no longer include stray header bytes - DIF / Numbers: no more debug messages printed to the console while parsing
- 433 automated tests (up from 425)
2.1.0 (2026-10-28)
Section titled “2.1.0 (2026-10-28)”Legacy Excel files (.xls) now come through much more complete: formulas that point to other sheets, named ranges, array formulas and external workbooks are decoded, formatted text no longer corrupts the cells after it, and write-protected workbooks open normally.
Highlights
Section titled “Highlights”- Formulas across sheets in .xls:
=Sheet2!A1,='My Sheet'!A1:B2, ranges over several sheets (=SUM(Jan:Dec!B2)) and references to other workbooks (='[Book2.xls]Prices'!A1). References to deleted sheets become#REF!. - Named ranges in .xls:
=SUM(Total), built-in names such asPrint_Area, names from other workbooks and add-in / user-defined functions (=MYUDF(A1)). - Array formulas in .xls: every cell of an array range gets its formula, and array constants such as
{1,2;"a",TRUE}are decoded. - Encrypted .xls: “read-only recommended” / write-protected workbooks open normally.
- XLS formulas: 3-D references (
tRef3d,tArea3d,tRefErr3d,tAreaErr3d) resolved through the workbook’s SUPBOOK and EXTERNSHEET records - XLS formulas: defined names, built-in names, external names and add-in functions (
tName,tNameX) - XLS formulas: array formulas (ARRAY record) and array constants (
tArray) - XLS formulas: memory tokens (
tMemArea,tMemFunc, …) and the full set of error values (#NULL!,#DIV/0!,#VALUE!,#REF!,#NAME?,#NUM!,#N/A) - Encrypted .xls: Office 97 RC4 workbooks protected with Excel’s default password are decrypted; other passwords, RC4 CryptoAPI and XOR obfuscation raise a clear
Workbook is encryptederror - TypeScript definitions for byte input (
Uint8Array,ArrayBuffer),options.extension,worksheetState,gridline,mediaanddefinedNames - HTML strings and DOM elements as input:
tabularjs('<table>...</table>') - 425 automated tests (up from 374)
- XLS text: rich-text and phonetic (Far East) strings broke the shared string table, so every later cell read the wrong text, and strings past the first 8 KB were lost. On 400 real-world workbooks compared with xlrd, value mismatches dropped from about 40% of the files to zero
- XLS sheets: a chart embedded in a worksheet cut the sheet short, and a chart sheet shifted the data of every following sheet
- XLS formula results: boolean, error and empty-string results were read as numbers; error cells now read
#DIV/0!and so on instead of#ERROR7 - XLS formulas: zero-argument functions (
NOW(),TODAY(),PI()) swallowed the preceding operand - XLS formulas: a formula that cannot be decoded exactly now shows the value Excel saved for the cell instead of
=ERRORor a wrong formula - XLS: inline strings written by older tools (LABEL records) were ignored
- HTML: every
<table>on a page now becomes a worksheet (only the first one was returned before); usetableIndexto pick one - CSV: the
encodingoption was ignored (files were always read as UTF-8) - TXT: a
delimiterpassed for.txtfiles was overridden by tab - Truncated .xls files: files cut short by a download now return the readable part instead of failing with
Sector out of bounds
2.0.0 (2026-10-09)
Section titled “2.0.0 (2026-10-09)”The package now imports correctly in Node.js, accepts raw bytes, and detects the format from the file content. It also adds protection against malicious files and ships a much smaller browser bundle.
Raw bytes and the extension option
Section titled “Raw bytes and the extension option”import tabularjs from 'tabularjs';
// From fetch, a database blob, an upload stream...const bytes = new Uint8Array(await (await fetch('/report')).arrayBuffer());
// The format is detected from the content...const result = await tabularjs(bytes);
// ...or forced explicitlyconst csv = await tabularjs(bytes, { extension: 'csv' });tabularjs(uint8Array | arrayBuffer | buffer)input, plus anoptions.extensionoverride- Content-based format detection (magic bytes) when the extension is missing or unrecognized: xlsx, ods, numbers, xls (OLE2), Lotus, dBase, SYLK, DIF, XML, HTML, CSV and TSV
- Apple Numbers (
.numbers) parser (experimental) - Zip-bomb protection on every xlsx, ods and numbers entry, and allocation limits on OLE2 containers (crafted
.xlsprotection) - 374 automated tests (up from 312)
- Packaging: the npm package could not be imported in Node.js at all. An
exportsmap now gives Node the ESM source, bundlers a real ES module (dist/index.mjs), and<script>tags the UMD build - XLS formulas: rows above 16384 decoded wrong, and ranges in shared formulas derailed parsing
- XLSX: images and charts referenced with several
../segments failed to resolve - Encoding: Cyrillic (windows-1251) and CJK text files were decoded as latin1 or utf-8, producing garbled text
Changed
Section titled “Changed”- Smaller browser bundle: 512 KiB to 196 KiB.
iconv-liteandchardetare now optional dependencies used only in Node.js for legacy DOS codepages
1.0.1 (2025-11-29)
Section titled “1.0.1 (2025-11-29)”First public release.
- Parsers for Excel 2007+ (
.xlsx), Excel 97-2003 (.xls), OpenDocument (.ods), XML Spreadsheet 2003, CSV/TSV, HTML tables, Lotus 1-2-3, SYLK, DIF and dBase - Automatic format detection by extension
- Formulas, merged cells, styles, comments, named ranges, hidden rows and columns, column widths and row heights
- Multiple worksheets and worksheet visibility
- Works in Node.js and the browser; output is directly compatible with Jspreadsheet CE and Pro

