Skip to content

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-format

Quick 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