Skip to main content

Install

npm install @jetstreamapp/simple-excel

Entry points​

ImportContents
@jetstreamapp/simple-excelEverything: createWorkbookWriter, openWorkbook, the sinks, sniff, sourceFrom, XlsxError and the types
@jetstreamapp/simple-excel/nodeThe same surface re-exported, plus fromFile, toFile, toWritable and nodeDeflater. See Node

Both entries ship ESM and CommonJS builds with TypeScript declarations. The core entry is browser-safe: it has no node: imports and no DOM references beyond TextEncoder, TextDecoder, CompressionStream, DecompressionStream and a duck-typed Blob, so it works unchanged in a Web Worker, an MV3 extension service worker and an Electron renderer.

import { collectToBytes, createWorkbookWriter, isXlsxError, openWorkbook, sniff, sourceFrom } from '@jetstreamapp/simple-excel';
import type { CellInput, CellStyle, OpenOptions, Workbook, WorkbookWriterOptions } from '@jetstreamapp/simple-excel';

Environment requirements​

FeatureUsed forMinimum
Web Streams (WritableStream)Stream sinks and the compression pumpChrome 103+, Firefox 113+, Safari 16.4+, Node 20+
CompressionStream('deflate-raw')Compressing zip entries on writeChrome 103+, Firefox 113+, Safari 16.4+, Node 20+
DecompressionStream('deflate-raw')Inflating zip entries on readsame
TextEncoder / TextDecoderUTF-8 on both sidesuniversal on the above
BlobcollectToBlob() onlyoptional; collectToBytes() needs no Blob

package.json declares engines.node >= 20.

caution

Node has had CompressionStream since 18, but it only accepts the deflate-raw format from 21.2. On an earlier Node the writer falls back to stored (uncompressed) parts; pass nodeDeflater() from the /node entry to keep files compressed — it uses zlib and works on every supported version. It is a good default for server-side writes anyway, because it lets you pick a compression level.

When CompressionStream is missing​

hasNativeDeflate() tells you whether the platform has it at all. If it does not, the writer stores every part uncompressed (zip method 0) instead of failing. The file is a valid .xlsx that every reader opens; it is just several times larger. In Node you can always get real compression by passing the zlib-backed deflater:

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

const workbook = createWorkbookWriter(toFile('out.xlsx'), {
deflater: hasNativeDeflate() ? undefined : nodeDeflater(),
});

collectToBlob() throws UNSUPPORTED_ENVIRONMENT where there is no Blob; use collectToBytes() or a stream sink there.

Verifying the install​

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

const sink = collectToBytes();
const workbook = createWorkbookWriter(sink);
const sheet = workbook.addSheet('Sheet1', { header: ['A', 'B'] });
await sheet.writeRow([1, 2]);
await sheet.close();
await workbook.close();
const bytes = sink.result(); // a Uint8Array holding a complete .xlsx

sink.result() on collectToBytes is synchronous and only valid after close() has resolved; on collectToBlob it returns a promise.