@office-open/xlsx
Generate and parse .xlsx spreadsheets with a declarative TypeScript API. Works in Node.js and browsers.
Features
- Workbook Generation — Create spreadsheets with multiple worksheets
- Cell Data — Strings, numbers, booleans, dates, and inline strings
- Styles — Fonts, fills, borders, alignment, and number formats via index-based style system
- Merged Cells — Merge cell ranges across rows and columns
- Column Width & Row Height — Custom column widths and row heights with hiding support
- Freeze Panes — Freeze rows and/or columns for scrollable headers
- Auto Filter — Add auto-filter dropdowns to column headers
- Images — Embed PNG and JPEG images anchored to cells
- Charts — Bar, line, pie, area, and scatter charts with customization
- Data Validation — List, whole number, decimal, date, and custom validations
- Conditional Formatting — Cell value-based rules with formatting
- Pivot Tables — Create pivot tables with various aggregation functions
- Comments — Cell comments with author tracking and rich text support
- Sheet & Workbook Protection — Password-protect worksheets and workbook structure
- Parsing — Parse existing .xlsx files with
parseWorkbookfor round-trip workflows - Template Patching — Patch existing XLSX templates via placeholder replacement
Installation
# pnpm
pnpm add @office-open/xlsx
# npm
npm install @office-open/xlsx
# yarn
yarn add @office-open/xlsx
# bun
bun add @office-open/xlsxQuick Start
import { generateWorkbook } from "@office-open/xlsx";
import { writeFileSync } from "node:fs";
const buffer = await generateWorkbook({
worksheets: [
{
name: "Sheet1",
rows: [
{ cells: [{ value: "Name" }, { value: "Score" }] },
{ cells: [{ value: "Alice" }, { value: 95 }] },
{ cells: [{ value: "Bob" }, { value: 87 }] },
],
},
],
});
writeFileSync("workbook.xlsx", buffer);Examples
Check the demo folder for working examples covering every feature.
Benchmark
Performance comparison against hucre (higher ops/s is better, Windows 11 / Node 24).
Default = XML DEFLATE level 1 (SuperFast); media is split by type, matching MS Office Excel — already-compressed formats (PNG/JPEG/GIF) are STOREd, the rest (EMF/WMF/BMP/TIFF/…) use DEFLATE level 6 / Normal (verified on a real MS Office file). All STORE = no compression ({ compression: { xml: 0, media: 0 } }). hucre (async only) uses CompressionStream("deflate-raw") when available, falls back to STORE per-entry when compression doesn't reduce size.
// Default (matches MS Office)
await generateWorkbook(options);
// All STORE (no compression)
await generateWorkbook(options, { compression: { xml: 0, media: 0 } });Create + toBuffer (end-to-end)
| Scenario | Default sync | Default async | All STORE sync | All STORE async | hucre |
|---|---|---|---|---|---|
| Simple (3 rows) | 2,040 ops/s | 1,096 ops/s | 21,165 ops/s | 21,851 ops/s | 854 ops/s |
| Styled rows (20) | 1,894 ops/s | 1,107 ops/s | 18,327 ops/s | 18,581 ops/s | 815 ops/s |
| Table (10x5) | 2,017 ops/s | 1,148 ops/s | 17,836 ops/s | 16,761 ops/s | 865 ops/s |
Large Files — Create + toBuffer
| Scenario | Default sync | Default async | All STORE sync | All STORE async | hucre |
|---|---|---|---|---|---|
| 2000 rows + 10 images | 110 ops/s | 127 ops/s | 127 ops/s | 134 ops/s | 43.7 ops/s |
| 200x10 table | 821 ops/s | 610 ops/s | 1,253 ops/s | 1,172 ops/s | 251 ops/s |
| 20 sheets × 100 rows + 20 img | 73.8 ops/s | 56.9 ops/s | 96 ops/s | 99 ops/s | 22.8 ops/s |
Large Data — 100,000 rows × 20 columns (2M cells)
| Scenario | Default sync | Default async | All STORE sync | All STORE async | hucre |
|---|---|---|---|---|---|
| 100k × 20 | 0.86 ops/s | 0.84 ops/s | 1.01 ops/s | 0.92 ops/s | 0.40 ops/s |