TabularJS vs SheetJS (xlsx)
SheetJS (the xlsx package) is the de-facto JavaScript Excel library — but it is also large, has a dual-license model, and exposes a low-level workbook/worksheet API that most applications end up wrapping. TabularJS offers the same core use case — reading a spreadsheet file into JSON — behind a single async call, with zero dependencies and a unified output across 16+ formats.
At a glance
| Feature | TabularJS | SheetJS (xlsx) |
|---|---|---|
| Dependencies | Zero | Bundled CFB + codepage + others |
| License | MIT | Apache 2.0 (Pro tier commercial) |
| Formats read | 16+ (XLSX, XLS, ODS, CSV, HTML, DBF, SYLK, DIF, Lotus) | Many, depending on build |
| API surface | Single await tabularjs(file) call | workbook → sheet → cell helpers |
| Output shape | Worksheet-first JSON, drop-in for Jspreadsheet | Workbook object, needs utils to convert |
| Write / mutate workbooks | Read-only | Yes |
| Runs in Node & browser | Yes | Yes |
Use SheetJS if you need to write workbooks, author formulas programmatically or do complex workbook manipulation. Use TabularJS if you only need to read files into JSON — that is the overwhelming majority of upload and ETL paths.
Migration: Node.js
A typical SheetJS read loop looks like this:
import * as XLSX from 'xlsx';
import fs from 'fs';
const buffer = fs.readFileSync('./sales.xlsx');
const workbook = XLSX.read(buffer, { type: 'buffer' });
const sheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[sheetName];
const rows = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
console.log(rows);The same thing with TabularJS is a single call:
import tabularjs from 'tabularjs';
const result = await tabularjs('./sales.xlsx');
console.log(result.worksheets[0].data);TabularJS detects the format from the path or file contents, so the same call works for .xlsx, .xls, .ods, .csv, .html and the rest.
Migration: browser upload
// With SheetJS
input.addEventListener('change', async (e) => {
const data = await e.target.files[0].arrayBuffer();
const workbook = XLSX.read(data);
const rows = XLSX.utils.sheet_to_json(
workbook.Sheets[workbook.SheetNames[0]],
{ header: 1 }
);
render(rows);
});// With TabularJS
input.addEventListener('change', async (e) => {
const result = await tabularjs(e.target.files[0]);
render(result.worksheets[0].data);
});No ArrayBuffer conversion, no sheet-name lookup, no utils.sheet_to_json.
Migration: rendering in Jspreadsheet
With SheetJS you typically convert cells, then re-map them into Jspreadsheet's data/columns shape. TabularJS returns that shape directly:
import jspreadsheet from 'jspreadsheet-ce';
import tabularjs from 'tabularjs';
input.addEventListener('change', async (e) => {
const result = await tabularjs(e.target.files[0]);
// The shape matches jspreadsheet() 1:1 — no mapping required
jspreadsheet(target, result);
});What you keep
- Formulas — preserved for XLSX, XLS, ODS, XML and SYLK.
- Merged cells and basic styles where the source supports them.
- A single JSON shape, identical across every format.
Ready to switch?
Install TabularJS and replace your SheetJS read calls in a single commit.

