Cookbook

Recipes

Working code for the things people actually want to do — open a workbook, build one from scratch, style cells, add a chart, stream millions of rows. Every snippet on this page is a real .ts file in the repo, type-checked against the live xlsx-kit on every build, so what you see compiles.

19 recipes across 6 categories. Just need the import line for one task? See the Cheatsheet. Looking up a specific function? Jump to the API reference.

Basics

# Open a workbook and read every cell

Load an existing xlsx, narrow the first sheet to a worksheet, and walk every cell.

site/src/lib/examples/recipes/open-and-iterate.ts .ts
// Open a workbook and walk every cell on the first sheet.

import { loadWorkbook } from 'xlsx-kit/io';
import { fromFile } from 'xlsx-kit/node';

const wb = await loadWorkbook(fromFile('input.xlsx'));
const first = wb.sheets[0];
if (first?.kind === 'worksheet') {
  for (const row of first.sheet.rows.values()) {
    for (const cell of row.values()) {
      console.log(`${cell.row},${cell.col}: ${String(cell.value)}`);
    }
  }
}
  • wb.sheets is a discriminated union — narrow on `kind === "worksheet"` to reach the Worksheet shape (chartsheets have a different surface).
  • For huge sheets, prefer `loadWorkbookStream` + `iterRows` instead — see the streaming recipe below.

# Edit a single cell and save

The canonical round-trip: load → mutate → write back. Same as the Quick start.

site/src/lib/examples/basic-read-write.ts .ts
// Read an xlsx, mutate one cell, write it back.
//
// This file is imported as ?raw into the docs site so the snippet shown to
// readers is exactly what svelte-check / tsc compiled — if an API rename
// breaks this import, the docs build fails before deploy.

import { loadWorkbook, workbookToBytes } from 'xlsx-kit/io';
import { fromBuffer } from 'xlsx-kit/node';
import { setCell } from 'xlsx-kit/worksheet';
import { readFile, writeFile } from 'node:fs/promises';

const wb = await loadWorkbook(fromBuffer(await readFile('input.xlsx')));
const ref = wb.sheets[0];
if (ref?.kind === 'worksheet') {
  setCell(ref.sheet, 1, 1, 'Hello from xlsx-kit');
}
await writeFile('output.xlsx', await workbookToBytes(wb));

# Build a workbook from scratch

No input file — start with `createWorkbook`, add a sheet, write cells, save.

site/src/lib/examples/recipes/build-from-scratch.ts .ts
// Build a one-sheet workbook from scratch and write it to disk.

import { saveWorkbook } from 'xlsx-kit/io';
import { toFile } from 'xlsx-kit/node';
import { addWorksheet, createWorkbook } from 'xlsx-kit/workbook';
import { setCell } from 'xlsx-kit/worksheet';

const wb = createWorkbook();
const ws = addWorksheet(wb, 'Quarterly');

setCell(ws, 1, 1, 'Quarter');
setCell(ws, 1, 2, 'Revenue');
setCell(ws, 2, 1, 'Q1');
setCell(ws, 2, 2, 12_400);
setCell(ws, 3, 1, 'Q2');
setCell(ws, 3, 2, 15_900);

await saveWorkbook(wb, toFile('quarterly.xlsx'));

# Multiple sheets + named ranges

Add several worksheets, define names that span them, and reference them in a formula.

site/src/lib/examples/recipes/multi-sheet.ts .ts
// Build several worksheets in one workbook and use named ranges
// to refer between them.

import { setFormula } from 'xlsx-kit/cell';
import { saveWorkbook } from 'xlsx-kit/io';
import { toFile } from 'xlsx-kit/node';
import { addDefinedName, addWorksheet, createWorkbook } from 'xlsx-kit/workbook';
import { setCell } from 'xlsx-kit/worksheet';

const wb = createWorkbook();
const inputs = addWorksheet(wb, 'Inputs');
const summary = addWorksheet(wb, 'Summary');

setCell(inputs, 1, 1, 'Revenue');
setCell(inputs, 1, 2, 100_000);
setCell(inputs, 2, 1, 'Cost');
setCell(inputs, 2, 2, 65_000);

addDefinedName(wb, { name: 'Revenue', value: 'Inputs!$B$1' });
addDefinedName(wb, { name: 'Cost', value: 'Inputs!$B$2' });

setCell(summary, 1, 1, 'Margin');
setFormula(setCell(summary, 1, 2), '(Revenue - Cost) / Revenue', { cachedValue: 0.35 });

await saveWorkbook(wb, toFile('multi-sheet.xlsx'));

# Direct fs helpers (Node)

`fromFile` / `toFile` skip the manual `readFile` / `writeFile` glue.

site/src/lib/examples/node-fs.ts .ts
// One-shot read + save direct from / to disk via the xlsx-kit/node
// helpers, no manual fs glue needed.

import { loadWorkbook, saveWorkbook } from 'xlsx-kit/io';
import { fromFile, toFile } from 'xlsx-kit/node';

const wb = await loadWorkbook(fromFile('input.xlsx'));
// ...mutate wb...
await saveWorkbook(wb, toFile('output.xlsx'));

Cells & values

# Style a header cell

Bold, font size, fill color, center alignment, and a thin border in five lines.

site/src/lib/examples/recipes/style-cells.ts .ts
// Apply font, fill, alignment, and a thin border to a header row.

import { saveWorkbook } from 'xlsx-kit/io';
import { toFile } from 'xlsx-kit/node';
import {
  centerCell,
  setBold,
  setCellBackgroundColor,
  setCellBorderAll,
  setFontSize,
} from 'xlsx-kit/styles';
import { addWorksheet, createWorkbook } from 'xlsx-kit/workbook';
import { setCell } from 'xlsx-kit/worksheet';

const wb = createWorkbook();
const ws = addWorksheet(wb, 'Report');

const header = setCell(ws, 1, 1, 'Total revenue');
setBold(wb, header);
setFontSize(wb, header, 12);
setCellBackgroundColor(wb, header, 'FFE0E7FF');
centerCell(wb, header);
setCellBorderAll(wb, header, { style: 'thin' });

await saveWorkbook(wb, toFile('styled.xlsx'));
  • These helpers are *cell-level* shortcuts. For range-wide changes, look at `setRangeFont`, `setRangeAlignment`, `setRangeBorderBox`, etc.
  • Background colors are hex `AARRGGBB` strings — leading `FF` is opaque alpha.

# Number formats: currency, percent, dates

`setCellAsCurrency` and `setCellAsPercent` are one-shot; everything else goes through `setCellNumberFormat` + a built-in or custom format code.

site/src/lib/examples/recipes/number-formats.ts .ts
// Apply number formats: currency, percentage, and a date-time.

import { saveWorkbook } from 'xlsx-kit/io';
import { toFile } from 'xlsx-kit/node';
import {
  FORMAT_DATE_DATETIME,
  setCellAsCurrency,
  setCellAsPercent,
  setCellNumberFormat,
} from 'xlsx-kit/styles';
import { addWorksheet, createWorkbook } from 'xlsx-kit/workbook';
import { setCell } from 'xlsx-kit/worksheet';

const wb = createWorkbook();
const ws = addWorksheet(wb, 'Numbers');

setCellAsCurrency(wb, setCell(ws, 1, 1, 12_400), { symbol: '$' });
setCellAsPercent(wb, setCell(ws, 1, 2, 0.187), 1);

const dateCell = setCell(ws, 1, 3, new Date('2026-05-08T09:30:00Z'));
setCellNumberFormat(wb, dateCell, FORMAT_DATE_DATETIME);

await saveWorkbook(wb, toFile('numbers.xlsx'));

# Add a formula (with cached value)

Pass `cachedValue` so Excel renders the result before forcing a full recalc on open.

site/src/lib/examples/recipes/formulas.ts .ts
// Set a formula. Optionally cache its evaluated value so Excel renders
// the result before recalculating on open.

import { setFormula } from 'xlsx-kit/cell';
import { saveWorkbook } from 'xlsx-kit/io';
import { toFile } from 'xlsx-kit/node';
import { addWorksheet, createWorkbook } from 'xlsx-kit/workbook';
import { setCell } from 'xlsx-kit/worksheet';

const wb = createWorkbook();
const ws = addWorksheet(wb, 'Sheet1');

setCell(ws, 1, 1, 12);
setCell(ws, 2, 1, 18);
setCell(ws, 3, 1, 30);

setFormula(setCell(ws, 4, 1), 'SUM(A1:A3)', { cachedValue: 60 });

await saveWorkbook(wb, toFile('with-formulas.xlsx'));
  • Cached values are optional — Excel will recalc anyway when the file opens, but cached values keep the file viewable in tools that don't recalc.
  • For shared and array formulas, use `setSharedFormula` / `setArrayFormula` from `xlsx-kit/cell` on the Cell returned by `setCell`.

# Merge cells + freeze the header row

Merge a title across columns, then freeze row 1 so it stays put while scrolling.

site/src/lib/examples/recipes/merge-and-freeze.ts .ts
// Merge a header range across the top row and freeze the first row so
// it stays visible while scrolling.

import { saveWorkbook } from 'xlsx-kit/io';
import { toFile } from 'xlsx-kit/node';
import { centerCell, setBold } from 'xlsx-kit/styles';
import { addWorksheet, createWorkbook } from 'xlsx-kit/workbook';
import {
  makeFreezePane,
  makeSheetView,
  mergeCells,
  setCell,
} from 'xlsx-kit/worksheet';

const wb = createWorkbook();
const ws = addWorksheet(wb, 'Report');

const title = setCell(ws, 1, 1, 'Q2 financial summary');
setBold(wb, title);
centerCell(wb, title);
mergeCells(ws, 'A1:E1');

ws.views.push(makeSheetView({ pane: makeFreezePane('A2') }));

await saveWorkbook(wb, toFile('merged-frozen.xlsx'));

Tables, validation, conditional formatting

# Promote a range to an Excel Table

Excel Tables get banded styling, a built-in autoFilter on every header, and a name you can reference in formulas.

site/src/lib/examples/recipes/tables-with-filter.ts .ts
// Promote a range to an Excel Table (named range with banded styling and
// a built-in filter dropdown on every header).

import { saveWorkbook } from 'xlsx-kit/io';
import { toFile } from 'xlsx-kit/node';
import { addWorksheet, createWorkbook } from 'xlsx-kit/workbook';
import { addExcelTable, setCell } from 'xlsx-kit/worksheet';

const wb = createWorkbook();
const ws = addWorksheet(wb, 'Inventory');

const headers = ['SKU', 'Name', 'Price'];
headers.forEach((h, i) => setCell(ws, 1, i + 1, h));
const rows: ReadonlyArray<readonly [string, string, number]> = [
  ['A-001', 'Widget', 19.95],
  ['A-002', 'Gadget', 24.5],
  ['A-003', 'Doohickey', 7.25],
];
rows.forEach((row, r) => row.forEach((v, c) => setCell(ws, r + 2, c + 1, v)));

addExcelTable(wb, ws, {
  name: 'Inventory',
  ref: 'A1:C4',
  columns: headers,
  style: 'TableStyleMedium2',
});

await saveWorkbook(wb, toFile('inventory.xlsx'));
  • Pass `style` for one-arg style selection or `styleInfo` for full control over banded rows / columns.
  • For just a filter without table styling, use `addAutoFilter(ws, "A1:C4")`.

# Heat-map with a 3-color scale

Build a `colorScale` rule with `makeCfRule` + inner XML and attach it via `addConditionalFormatting`.

site/src/lib/examples/recipes/conditional-color-scale.ts .ts
// Color-scale rule: red for low values, yellow for the middle, green
// for high. Excel's classic 3-color heat-map.

import { saveWorkbook } from 'xlsx-kit/io';
import { toFile } from 'xlsx-kit/node';
import { addWorksheet, createWorkbook } from 'xlsx-kit/workbook';
import {
  addConditionalFormatting,
  makeCfRule,
  makeConditionalFormatting,
  setCell,
} from 'xlsx-kit/worksheet';

const wb = createWorkbook();
const ws = addWorksheet(wb, 'Heat');

for (let r = 1; r <= 10; r++) setCell(ws, r, 1, Math.round(Math.random() * 100));

addConditionalFormatting(
  ws,
  makeConditionalFormatting({
    sqref: 'A1:A10',
    rules: [
      makeCfRule({
        type: 'colorScale',
        priority: 1,
        formulas: [],
        innerXml:
          '<colorScale>' +
          '<cfvo type="min"/><cfvo type="percentile" val="50"/><cfvo type="max"/>' +
          '<color rgb="FFF8696B"/><color rgb="FFFFEB84"/><color rgb="FF63BE7B"/>' +
          '</colorScale>',
      }),
    ],
  }),
);

await saveWorkbook(wb, toFile('heatmap.xlsx'));

Charts & images

# Add a clustered column chart

Wire a `BarChart` to a data range and anchor it to a cell with `addChartAt`.

site/src/lib/examples/recipes/add-bar-chart.ts .ts
// Add a clustered column chart driven by a data range on the same sheet.

import { makeBarChart, makeBarSeries, makeChartSpace } from 'xlsx-kit/chart';
import { addChartAt } from 'xlsx-kit/drawing';
import { saveWorkbook } from 'xlsx-kit/io';
import { toFile } from 'xlsx-kit/node';
import { addWorksheet, createWorkbook } from 'xlsx-kit/workbook';
import { setCell } from 'xlsx-kit/worksheet';

const wb = createWorkbook();
const ws = addWorksheet(wb, 'Sales');

setCell(ws, 1, 1, 'Region');
setCell(ws, 1, 2, 'Revenue');
setCell(ws, 2, 1, 'NA');
setCell(ws, 2, 2, 12_400);
setCell(ws, 3, 1, 'EU');
setCell(ws, 3, 2, 9_800);
setCell(ws, 4, 1, 'APAC');
setCell(ws, 4, 2, 7_300);

const chart = makeBarChart({
  barDir: 'col',
  grouping: 'clustered',
  series: [
    makeBarSeries({
      idx: 0,
      tx: { kind: 'literal', value: 'Revenue' },
      cat: { ref: 'Sales!$A$2:$A$4' },
      val: { ref: 'Sales!$B$2:$B$4' },
    }),
  ],
});

const space = makeChartSpace({
  plotArea: { chart },
  title: 'Revenue by region',
  legend: { position: 'r' },
});

addChartAt(ws, 'D2', { space }, { widthPx: 480, heightPx: 320 });

await saveWorkbook(wb, toFile('chart.xlsx'));
  • Same pattern works for `makeLineChart`, `makePieChart`, `makeScatterChart` and friends — wrap them in a `PlotArea` and pass to `makeChartSpace`.
  • For modern chart kinds (Sunburst, Treemap, Waterfall, Histogram, Pareto, Funnel, BoxWhisker, RegionMap), use the `makeSunburstChart` / `makeTreemapChart` / ... helpers from the chartex family — they emit `cx:` chart space.

# Insert an image at a cell

Drop a PNG / JPEG / GIF / BMP / WebP / TIFF / SVG anchored to a cell — format and dimensions are auto-detected.

site/src/lib/examples/recipes/insert-image.ts .ts
// Insert a PNG / JPEG image at a cell anchor. Format and dimensions
// are auto-detected from the bytes, so loadImage is the only call.

import { addImageAt, loadImage } from 'xlsx-kit/drawing';
import { saveWorkbook } from 'xlsx-kit/io';
import { toFile } from 'xlsx-kit/node';
import { addWorksheet, createWorkbook } from 'xlsx-kit/workbook';
import { readFile } from 'node:fs/promises';

const wb = createWorkbook();
const ws = addWorksheet(wb, 'Cover');

const image = loadImage(await readFile('logo.png'));
addImageAt(ws, 'B2', image, { widthPx: 200, heightPx: 80 });

await saveWorkbook(wb, toFile('with-image.xlsx'));

Streaming (huge sheets)

# Write 10M rows in a fixed memory budget

`createWriteOnlyWorkbook` deflates each row as it arrives — heap stays under 100 MB even for 10M-row sheets.

site/src/lib/examples/streaming-write.ts .ts
// Stream millions of rows to disk in a fixed memory budget. Each row is
// deflated as it arrives — no intermediate workbook in memory.

import { toFile } from 'xlsx-kit/node';
import { createWriteOnlyWorkbook } from 'xlsx-kit/streaming';

const sink = toFile('big.xlsx');
const wb = await createWriteOnlyWorkbook(sink);
const ws = await wb.addWorksheet('Data');
ws.setColumnWidth(1, 24); // must precede the first appendRow

for (let r = 0; r < 10_000_000; r++) {
  await ws.appendRow([r, `row-${r}`, r * Math.PI]);
}
await ws.close();
await wb.finalize();
  • `setColumnWidth` must run *before* the first `appendRow` — once any row is written, `<cols>` is locked.
  • `ws.close()` and `wb.finalize()` are required — that's when the central directory is written.

# Iterate a huge sheet without loading it

`loadWorkbookStream` + `iterRows` walks the file once and yields rows as they're parsed.

site/src/lib/examples/streaming-read.ts .ts
// Iterate huge sheets without loading the full workbook. iterRows is a SAX
// pass — it walks the file once and yields each row's cells.

import { fromFile } from 'xlsx-kit/node';
import { loadWorkbookStream } from 'xlsx-kit/streaming';

const wb = await loadWorkbookStream(fromFile('big.xlsx'));
const sheet = wb.openWorksheet(wb.sheetNames[0] ?? '');
for await (const row of sheet.iterRows({ minRow: 1, maxRow: 100 })) {
  console.log(row.map((c) => c.value));
}
await wb.close();
  • Bound the walk with `iterRows({ minRow, maxRow, minCol, maxCol })` — the parser skips ahead via tag-scan.

Browser

# Browser: read xlsx from a fetch response

`fromResponse` is streaming, so the workbook starts parsing while bytes are still arriving.

site/src/lib/examples/browser-fetch.ts .ts
// Browser: pipe a fetch Response straight into the loader. fromResponse is
// streaming, so the workbook starts parsing before the download is done.

import { fromResponse, loadWorkbook } from 'xlsx-kit/io';

const response = await fetch('/sheet.xlsx');
const wb = await loadWorkbook(fromResponse(response));
const ref = wb.sheets[0];
if (ref?.kind === 'worksheet') {
  console.log(ref.sheet.title);
}

# Browser: read xlsx from <input type="file">

`fromBlob` consumes the File the user just picked, no full buffer.

site/src/lib/examples/recipes/browser-file-input.ts .ts
// Browser: parse the xlsx the user just selected via <input type="file">.
// fromBlob is streaming, so the workbook starts parsing while the file
// is still being read.

import { fromBlob, loadWorkbook } from 'xlsx-kit/io';

export async function loadFromInput(input: HTMLInputElement) {
  const file = input.files?.[0];
  if (!file) return null;
  const wb = await loadWorkbook(fromBlob(file));
  return wb;
}