# xlsx-format

[![Powered by Sebastian Software](https://img.shields.io/badge/Powered%20by-Sebastian%20Software-00718d?style=flat-square)](https://oss.sebastian-software.com)
[![npm version](https://img.shields.io/npm/v/xlsx-format)](https://www.npmjs.com/package/xlsx-format)
[![CI](https://github.com/sebastian-software/xlsx-format/actions/workflows/ci.yml/badge.svg)](https://github.com/sebastian-software/xlsx-format/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/sebastian-software/xlsx-format/graph/badge.svg)](https://codecov.io/gh/sebastian-software/xlsx-format)
[![license](https://img.shields.io/npm/l/xlsx-format)](LICENSE)
[![node](https://img.shields.io/node/v/xlsx-format)](https://nodejs.org/)
[![bun](https://img.shields.io/badge/Bun-tested-f9f1e1?logo=bun)](https://bun.sh/)
[![browser](https://img.shields.io/badge/Browser-supported-4285F4?logo=googlechrome&logoColor=white)](https://developer.mozilla.org/en-US/docs/Web/API)
[![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](https://www.typescriptlang.org/)

The XLSX library your bundler will thank you for. Zero dependencies. Promise-based read and write APIs. Works in Node.js and the browser.
Use it for simple data conversion, or replace ExcelJS for styled browser report exports without carrying a workbook framework just for formatting.

**[Documentation](https://sebastian-software.github.io/xlsx-format/)** | **[API Reference](https://sebastian-software.github.io/xlsx-format/api-reference)**

```bash
npm install xlsx-format
```

```typescript
import { readFile, writeFile } from "node:fs/promises";
import { read, write, sheetToJson, jsonToSheet, createWorkbook } from "xlsx-format";

// Read an Excel file into JSON
const workbook = await read(await readFile("report.xlsx"));
const rows = sheetToJson(workbook.Sheets[workbook.SheetNames[0]]);

// Write JSON back to Excel
const sheet = jsonToSheet([
	{ Name: "Alice", Revenue: 48000 },
	{ Name: "Bob", Revenue: 52000 },
]);
await writeFile("output.xlsx", await write(createWorkbook(sheet, "Q4 Sales")));
```

## Why xlsx-format?

Most projects just need XLSX. A focused library keeps the bundle and workbook API small while leaving file-system I/O to the host application.

xlsx-format does one thing well: read and write modern Excel files. The result is a library you can actually tree-shake, `await`, and ship to the browser without a separate bundle.

|                     | **xlsx-format 2.4.2**          | **SheetJS 0.18.12**      | **ExcelJS 4.4.0**                |
| ------------------- | ------------------------------ | ------------------------ | -------------------------------- |
| **Written in**      | TypeScript (strict)            | JavaScript with typings  | JavaScript with TypeScript types |
| **Async**           | Promise-based API              | Synchronous public API   | Promise-based selected APIs      |
| **Module format**   | ESM + CJS                      | ESM + CJS                | CJS + browser build              |
| **Tree-shakeable**  | Yes                            | Limited                  | Limited                          |
| **Browser support** | Yes (`read` / `write`)         | Yes (separate bundle)    | Yes (`exceljs.browser.js`)       |
| **Formats**         | XLSX / XLSM / CSV / TSV / HTML | Many spreadsheet formats | XLSX / CSV                       |
| **Styled reports**  | Yes                            | Yes                      | Yes                              |
| **API style**       | Named exports                  | Namespace object         | Class-based                      |
| **License**         | Apache 2.0                     | Apache 2.0               | MIT                              |

The version labels identify the snapshots used for this orientation table. The [SheetJS package metadata](https://github.com/SheetJS/sheetjs/blob/master/package.json) advertises ESM/CJS entry points and browser bundles. ExcelJS documents its browser build and workbook API in its [browser documentation](https://github.com/exceljs/exceljs#browser).

For a detailed feature matrix (cell data, formulas, styles, comments, hyperlinks, and more), see [Why xlsx-format?](https://sebastian-software.github.io/xlsx-format/guide/why-xlsx-format) in the docs.

XLSM support covers workbook cell data and macro-free output containers. VBA projects are not read, preserved, or written, so do not use an XLSM read/write round trip when macros must survive unchanged.

## Runs everywhere

xlsx-format is fully platform-agnostic -- it never imports `node:fs` or any other Node.js built-in. This means it works out of the box in browsers, edge runtimes (Cloudflare Workers, Deno Deploy), and Node.js without bundler polyfills.

**Node.js** -- Pair `read()` / `write()` with Node's `fs` module:

```typescript
import { readFile, writeFile } from "node:fs/promises";
import { read, write } from "xlsx-format";

const wb = await read(await readFile("input.xlsx"));
await writeFile("output.xlsx", await write(wb));
```

**Browsers** -- Use the File API or fetch:

```typescript
import { read, write } from "xlsx-format";

// Read from a file input
const wb = await read(await file.arrayBuffer());

// Trigger a download
const blob = new Blob([await write(wb, { type: "array" })], {
	type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "output.xlsx";
link.click();
```

## Security

For vulnerability reports and supported versions, see the [Security Policy](SECURITY.md). The [Security Considerations guide](https://sebastian-software.github.io/xlsx-format/guide/security) covers untrusted uploads, export safety, and recommended deployment patterns.

When exporting user-controlled data, the built-in guards for spreadsheet formulas and unsafe HTML links are enabled by default. Keep those defaults for untrusted data; set the options to `false` only when you need exact text or link fidelity and control the output destination:

```typescript
import { sheetToCsv, sheetToHtml } from "xlsx-format";

const csv = sheetToCsv(sheet); // escapeFormulae defaults to true
const html = sheetToHtml(sheet); // sanitizeLinks defaults to true
```

Reads are bounded by configurable limits for ZIP entries, total uncompressed bytes, per-entry bytes, XML text, and worksheet dimensions. Keep the defaults for untrusted uploads; raise `ReadOptions` limits explicitly only for large trusted files.

## Styled report exports

Need polished XLSX output, but not the full ExcelJS object model? xlsx-format can write styled workbook reports with fonts, fills, borders, number formats, row heights, column widths, merged title rows, and frozen panes. Styling is opt-in with `cellStyles: true`, so existing unstyled exports keep their lean output.

```typescript
import {
	arrayToSheet,
	createWorkbook,
	freezePanes,
	mergeCells,
	setCellStyle,
	setColumnWidth,
	setRowHeight,
	styleRange,
	write,
	type CellStyle,
} from "xlsx-format";

const titleStyle: CellStyle = {
	font: { name: "Calibri", size: 14, bold: true, color: { argb: "FFFFFFFF" } },
	fill: { patternType: "solid", fgColor: { argb: "FF1F4E79" } },
	alignment: { vertical: "middle" },
};

const headerStyle: CellStyle = {
	font: { bold: true, color: { argb: "FFFFFFFF" } },
	fill: { patternType: "solid", fgColor: { argb: "FF2E75B6" } },
	alignment: { horizontal: "center", vertical: "middle", wrapText: true },
};

const ws = arrayToSheet([
	["Northstar Solar PPA - Q2 Report", null, null],
	["Month", "Expected MWh", "Settlement"],
	["Apr 2026", 12400, -18350],
]);

setCellStyle(ws["A1"], titleStyle);
styleRange(ws, "A2:C2", headerStyle);
mergeCells(ws, "A1:C1");
setRowHeight(ws, 0, 30);
setColumnWidth(ws, 0, 18);
freezePanes(ws, { ySplit: 2 });

const bytes = await write(createWorkbook(ws, "Overview"), {
	type: "array",
	cellStyles: true,
});
```

See the [Styled Workbooks guide](https://sebastian-software.github.io/xlsx-format/guide/styled-workbooks) for a full report export example.

## Switching from SheetJS

The API is intentionally close to SheetJS. Three things change:

1. `read()` and `write()` return Promises. ZIP compression and decompression use Web Streams, while XML parsing, worksheet traversal, and final outputs use synchronous in-memory work. Move large or latency-sensitive jobs to a worker.
2. Named imports replace the namespace: `import { read } from "xlsx-format"`
3. Utility names are camelCase: `sheetToJson` instead of `XLSX.utils.sheet_to_json`

```diff
- import XLSX from "xlsx";
+ import { read, write, sheetToJson, sheetToCsv } from "xlsx-format";

- const wb = XLSX.read(buffer);
+ const wb = await read(buffer);

- const rows = XLSX.utils.sheet_to_json(ws);
+ const rows = sheetToJson(ws);

- const buf = XLSX.write(wb, { type: "buffer", bookType: "xlsx" });
+ const buf = await write(wb, { type: "buffer" });
```

Cell objects keep the same shape: `{ t: "n", v: 42, w: "42" }` works exactly as before. For a full function mapping table, see the [Migration Guide](https://sebastian-software.github.io/xlsx-format/guide/migration).

## Acknowledgments

Based on the work of [SheetJS](https://github.com/SheetJS/sheetjs), originally created by SheetJS LLC. Thank you to the SheetJS team and its contributors for building the foundation this library stands on.

## License

Apache 2.0 -- see [LICENSE](LICENSE) for details.

Copyright (C) 2012-present SheetJS LLC (original work)

---

<!-- sebastian-software-branding:start -->
<p align="center">
  <a href="https://oss.sebastian-software.com">
    <img src="https://sebastian-brand.vercel.app/sebastian-software/logo-software.svg" alt="Sebastian Software" width="240" />
  </a>
</p>

<p align="center">
  <a href="https://oss.sebastian-software.com">Open Source at Sebastian Software</a><br />
  Copyright &copy; 2025&ndash;2026 Sebastian Software GmbH
</p>
<!-- sebastian-software-branding:end -->
