Getting Started
Install xlsx-format and start reading and writing Excel files in minutes.
Installation
# npm
npm install xlsx-format
# pnpm
pnpm add xlsx-format
# yarn
yarn add xlsx-format
# bun
bun add xlsx-formatQuick Start
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")));Reading Files
Node.js
import { readFile } from "node:fs/promises";
import { read } from "xlsx-format";
// XLSX from a file
const workbook = await read(await readFile("spreadsheet.xlsx"));
// CSV / HTML from a file (pass as string)
const workbook = await read(await readFile("data.csv", "utf-8"), { type: "string" });
// TSV uses the same text reader with an explicit field separator
const tabular = await read(await readFile("data.tsv", "utf-8"), { type: "string", FS: "\t" });
// From a Uint8Array or ArrayBuffer
const workbook = await read(buffer);Browser
import { read } from "xlsx-format";
// From a File input
const buffer = await file.arrayBuffer();
const workbook = await read(buffer);
// From a fetch response
const response = await fetch("/data/report.xlsx");
const workbook = await read(new Uint8Array(await response.arrayBuffer()));Writing Files
Node.js
import { writeFile } from "node:fs/promises";
import { write } from "xlsx-format";
// XLSX to a file
await writeFile("output.xlsx", await write(workbook));
// CSV to a file
await writeFile("output.csv", await write(workbook, { bookType: "csv", type: "string" }));
// HTML to a file
await writeFile("output.html", await write(workbook, { bookType: "html", type: "string" }));Browser
import { write } from "xlsx-format";
// Trigger a download
const data = await write(workbook, { type: "array" });
const blob = new Blob([data], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "output.xlsx";
link.click();write() returns Uint8Array for XLSX and XLSM output. This includes { type: "string" }, which is retained as a compatibility input but does not convert a ZIP-based workbook to text. For CSV, TSV, and HTML, the default and type: "string" return a string. type: "base64" always returns a string, while type: "array" always returns a Uint8Array.
type: "buffer" returns a Node.js Buffer when Buffer is available. Browsers and other runtimes receive a Uint8Array. The portable return type is therefore Uint8Array; use Buffer.isBuffer(data) when Node-specific behavior matters.
Reading Metadata Without Worksheets
Use bookSheets or bookProps when you only need workbook metadata. For XLSX input, these modes skip worksheet data and return a smaller object. Plain-text input is parsed before the same result projection is applied:
const { SheetNames } = await read(buffer, { bookSheets: true });
const { Props, Custprops } = await read(buffer, { bookProps: true });
const metadata = await read(buffer, { bookSheets: true, bookProps: true });The result of a metadata-only read does not contain Sheets. Literal options infer the exact result shape. If the options are held in a broadly typed ReadOptions object, the return type is a union because the flags can change at runtime; narrow it with "Sheets" in result before accessing worksheet data. The same result shapes apply to CSV and HTML text input.
Converting Data
xlsx-format converts between sheets and common data formats in both directions.
import {
sheetToJson,
jsonToSheet,
arrayToSheet,
sheetToArray,
sheetToCsv,
sheetToHtml,
csvToSheet,
htmlToSheet,
} from "xlsx-format";
// Sheet -> JSON objects (first row = headers)
const rows = sheetToJson(sheet);
// [{ Name: "Alice", Age: 30 }, { Name: "Bob", Age: 25 }]
// Sheet -> array of arrays
const arrays = sheetToArray(sheet);
// [["Name", "Age"], ["Alice", 30], ["Bob", 25]]
// Sheet -> CSV / HTML
const csv = sheetToCsv(sheet);
const html = sheetToHtml(sheet);
// JSON / arrays / CSV / HTML -> Sheet
const sheet1 = jsonToSheet([{ Name: "Alice", Age: 30 }]);
const sheet2 = arrayToSheet([
["Name", "Age"],
["Alice", 30],
]);
const sheet3 = csvToSheet("Name,Age\nAlice,30");
const sheet4 = htmlToSheet("<table><tr><td>Name</td></tr></table>");Workbook Helpers
import { createWorkbook, appendSheet, setSheetVisibility } from "xlsx-format";
const wb = createWorkbook(firstSheet, "Sheet1");
appendSheet(wb, secondSheet, "Sheet2");
setSheetVisibility(wb, 1, 1);Cell Utilities
import { setCellNumberFormat, setCellHyperlink, addCellComment, setArrayFormula } from "xlsx-format";
setCellNumberFormat(sheet["B2"], "#,##0.00");
setCellHyperlink(sheet["A1"], "https://example.com");
addCellComment(sheet["C3"], "Check this value", "Alice");
setArrayFormula(sheet, "D1:D10", "=A1:A10*B1:B10");Use formatCell when you need the displayed value for one cell without
converting a whole sheet.
import { formatCell } from "xlsx-format";
const displayValue = formatCell(sheet["B2"]);Styled Workbooks
Use styled writing when you need polished report exports: branded title rows, table headers, totals, merged cells, row heights, column widths, and frozen panes. This is the lightweight path for replacing ExcelJS in browser exports without cloning the ExcelJS class API.
import { arrayToSheet, createWorkbook, styleRange, mergeCells, freezePanes, write } from "xlsx-format";
const sheet = arrayToSheet([
["Northstar Solar PPA - Q2 Report", null, null],
["Month", "Expected MWh", "Settlement"],
["Apr 2026", 12400, -18350],
]);
sheet["A1"].s = {
font: { bold: true, color: { argb: "FFFFFFFF" } },
fill: { patternType: "solid", fgColor: { argb: "FF1F4E79" } },
};
styleRange(sheet, "A2:C2", {
font: { bold: true, color: { argb: "FFFFFFFF" } },
fill: { patternType: "solid", fgColor: { argb: "FF2E75B6" } },
alignment: { horizontal: "center", wrapText: true },
});
mergeCells(sheet, "A1:C1");
freezePanes(sheet, { ySplit: 2 });
const data = await write(createWorkbook(sheet, "Overview"), {
type: "array",
cellStyles: true,
});See Styled Workbooks for a full report export example.
Cell Addresses
import { decodeCell, encodeCell, decodeRange, encodeRange } from "xlsx-format";
decodeCell("B3"); // { r: 2, c: 1 }
encodeCell({ r: 2, c: 1 }); // "B3"
decodeRange("A1:C5"); // { s: { r: 0, c: 0 }, e: { r: 4, c: 2 } }
encodeRange(range); // "A1:C5"Runtime Support
Node.js >= 22 -- Use read() with fs.readFile() and write() with fs.writeFile() from node:fs/promises for file I/O.
Browsers -- read() and write() work in any modern browser with Uint8Array or ArrayBuffer. No Node.js APIs needed.
Next Steps
- Styled Workbooks -- Build report exports that can replace ExcelJS styling flows
- Security Considerations -- Safely parse untrusted files and export user-controlled data
- Why xlsx-format? -- See how xlsx-format compares to SheetJS and ExcelJS
- Migration Guide -- Step-by-step guide for switching from SheetJS
- API Reference -- Full API documentation