npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jetstreamapp/simple-excel

v0.1.0

Published

Streaming, dependency-free xlsx reader and writer for browsers and Node. Built for large files that must open cleanly in Excel, Google Sheets, LibreOffice and Numbers.

Readme

simple-excel

Streaming, dependency-free xlsx reader and writer for browsers and Node.

Status: pre-release, under active development. The API below is the target; CHANGELOG.md records what has landed. The library is built against a committed corpus of real files and a compatibility oracle that includes Excel itself.

@jetstreamapp/simple-excel writes a workbook by pushing rows into a sink as they arrive, and reads one by pulling rows through an async iterator, so memory stays flat whether the sheet has a thousand rows or a million. There are no runtime dependencies: the zip container, the deflate stream, the XML tokenizer and the SpreadsheetML layer are all in the package, and compression is the platform's own CompressionStream.

Why another xlsx library

| | simple-excel | SheetJS CE | ExcelJS | @office-kit/xlsx | write-excel-file / read-excel-file | | ------------------------------------------- | ---------------------- | --------------------------------- | ------------------- | ---------------------------------- | ---------------------------------- | | Streaming write (flat memory at 1M rows) | yes | no | yes | no (buffers until finalize()) | no | | Streaming read | yes | no | yes | yes | no | | Runtime dependencies | 0 | 0 | 9 | 3 | 3 each | | Bundle size (min+brotli) | 24.9 KB | not measured | not measured | ≤ 120 KB (its README) | not measured | | Maintained on npm | yes | no (fixes on the vendor CDN only) | inactive since 2023 | yes (pre-1.0) | yes | | Verified against Excel with a public corpus | yes | no | no | no (validator + fixtures in CI) | no | | Styles (fonts, fills, borders, number fmts) | write | Pro only | yes | yes | basic, write only | | Formulas, charts, pivots, editing workbooks | no | partial | partial | yes | no | | Legacy formats (.xls, .xlsb, .ods, CSV) | no, detected and named | yes, silently | CSV only | no, every CFB reported "encrypted" | no |

Sources: dependency counts are each package's dependencies field at the versions pinned in package.json; maintenance and architecture come from research/03-library-landscape.md; the office-kit column is research/10-office-kit-evaluation.md, which found that its streaming writer buffers the worksheet and an unbounded shared-string table until finalize() (RangeError: Invalid string length at 1M × 20 — the same wall SheetJS hits). Only office-kit publishes a bundle figure; the other libraries have not been measured here. Ours is dist/esm/index.mjs compressed at brotli quality 11, checked on every build by npm run size. "Verified against Excel with a public corpus" means a committed fixture corpus plus an oracle that opens every written file in Excel and fails on a repair prompt — see research/05-compatibility-matrix.md.

Pick simple-excel when you export or import tabular data and it has to be large, fast and correct. Pick a full-featured library when you edit existing workbooks or need charts, pivots and formulas.

Measured

100,000 rows × 20 columns of Salesforce-shaped data (ids, unicode names, decimals, booleans, dates, long text, JSON blobs, 10% nulls — about 1.7 KB of text per row), Apple M4, Node v24.18.0, median of three runs in a fresh process. Full tables and method: research/06-performance-baseline.md, runs bench/results/2026-09-12-macbook-air-inline-default (100k rows, the default configuration) and …-phase-e-optimized / …-phase-e-scale / …-phase-e-chrome (1M rows, 18M cells and Chrome, measured with the optional bounded shared-string table, strings: 'auto', before inline became the default).

| | simple-excel | SheetJS 0.20.3 | | --------------------------- | ---------------------------- | ----------------------------------- | | Write | 3.08 s | 9.75 s | | Write peak memory over idle | 58 MB | 3,313 MB | | Read (typed records) | 1.64 s | 6.17 s | | Read peak memory over idle | 810 MB | 1,837 MB | | First byte at the sink | 0.5 ms | n/a (nothing until the end) | | 1,000,000 × 20 write | 31.9 s, 77 MB of heap growth | RangeError: Invalid string length | | 18M cells (900,000 × 20) | 27.0 s, 76 MB of heap growth | RangeError: Invalid string length |

The 810 MB read figure is the 100,000 materialized records the typed read returns, not the parser: streaming the same file with sheet.rows() costs 1.48 s and 46 MB. With strings: 'auto' (the bounded shared-string table) the write is 2.77 s: fewer bytes reach the compressor, which is the bottleneck.

In a Chrome module worker (Chromium 153, writing to collectToBlob()), simple-excel writes 100k × 20 in 3.88 s for +236 MB of renderer RSS and 1,000,000 × 20 in 39.6 s for +254 MB — flat across a 10× row increase. SheetJS takes 7.64 s and +3,039 MB at 100k and crashes the renderer at 1M (bench/results/2026-09-12-macbook-air-phase-e-chrome).

In Node, passing the /node entry's nodeDeflater(1) instead of the platform CompressionStream cuts write time by about 44% (1.73 s at 100k, 19.1 s at 1M) for a file about 20% larger.

Install

npm install @jetstreamapp/simple-excel

Write

import { collectToBlob, createWorkbookWriter } from '@jetstreamapp/simple-excel';

const sink = collectToBlob();
const workbook = createWorkbookWriter(sink);
const sheet = workbook.addSheet('Accounts', {
  header: ['Id', 'Name', 'Created'],
  freeze: { rows: 1 },
  autoFilter: true,
});

for await (const record of records) {
  await sheet.writeRow([record.Id, record.Name, record.CreatedDate]);
}

await sheet.close();
const result = await workbook.close();
const blob = await sink.result();
console.log(`${result.bytes} bytes, ${result.sheets[0]?.rows} rows`);

One sheet is open at a time: close it before adding the next. Cell values are string | number | boolean | Date | null, plus undefined, bigint and { error: '#N/A' }.

Read

import { openWorkbook } from '@jetstreamapp/simple-excel';

const workbook = await openWorkbook(file); // File | Blob | ArrayBuffer | Uint8Array | RandomAccessSource

for (const { name, index, kind, hidden } of workbook.sheets) {
  console.log(index, name, kind, hidden);
}

const sheet = workbook.sheet('Accounts');
for await (const row of sheet.rows({ mode: 'object' })) {
  // row is Record<string, string | number | boolean | Date | null>
}

await workbook.close();

rows() streams; nothing but the shared-string table stays resident. sheet.head(5) returns the first rows as an A1-keyed map without inflating the rest of the sheet, which is how you inspect a template before reading it.

Node

import { createWorkbookWriter, fromFile, nodeDeflater, toFile } from '@jetstreamapp/simple-excel/node';

const workbook = createWorkbookWriter(toFile('accounts.xlsx'), { deflater: nodeDeflater(1) });
const source = await fromFile('upload.xlsx');

The /node entry re-exports everything from the core entry and adds file-backed sources and sinks plus a zlib-backed deflater with a compression level (the platform CompressionStream has no level).

Browser support

The core entry needs Web Streams and CompressionStream('deflate-raw'): Chrome 103+, Firefox 113+, Safari 16.4+ and Node 20+. Where CompressionStream is absent entirely the writer falls back to storing parts uncompressed — the file is still a valid xlsx, only larger. Node only accepts the deflate-raw format from 21.2, so on Node 20 the writer falls back to stored parts too; pass the Node entry's nodeDeflater() there to keep files compressed.

No DOM API is used beyond TextEncoder/TextDecoder and a duck-typed Blob, so the library runs unchanged in a Web Worker, an MV3 service worker and an Electron renderer.

What it does not do

Not in v1 (from research/07-reference-architecture.md §9): writing or evaluating formulas; rendering number formats; hyperlinks, comments, images, charts, tables, conditional formatting, data validation; editing an existing workbook or preserving unknown parts through a round trip; opening encrypted files (they are detected and named, not decrypted); .xls, .xlsb, .ods, SpreadsheetML 2003 and HTML; CSV parsing; theme or indexed colour resolution on read; reading from a non-seekable stream; a JavaScript inflate/deflate; rich-text writing; column-level styles; print setup; sheet protection.

Documentation

The user documentation site is in docs/. The design behind the library is in research/: the format primer, the edge-case catalog, the compatibility matrix across eight readers, the performance baseline and the architecture decision records.

License

MIT