Skip to content

@marcusok/excel-preview ​

Read-only xlsx preview in the browser, built on the same WASM engine as @marcusok/excel-exporter: parsing runs in a Web Worker, rendering is a framework-agnostic DOM grid with virtual scrolling, and a dedicated compensation layer restores Excel-accurate number formatting and theme colors.

Capabilities ​

CapabilityDescription
Formats.xlsx / .xlsm (ZIP/OOXML), password-protected workbooks (password option), and .csv (UTF-8 / GB18030, delimiter sniffing)
Worker parsingThe whole parse + model build runs in a shared Web Worker (~1.5s for 100k × 10 cells, measured; UI never freezes)
Virtual scrollingDOM renderer mounts only viewport cells (plus a buffer); merged cells spanning the viewport are recalled as a whole
Layout fidelityColumn widths, row heights, hidden rows/columns, merged cells, frozen panes (four-quadrant layers), sheet tabs (hidden sheets kept out)
Style fidelityFonts, solid / pattern / gradient fills, borders, alignment (wrap / indent / rotation), grid-line switch from the file
Theme & indexed colorsRecovered by a self-parsing overlay over styles.xml + theme1.xml — the engine's read path drops theme/tint/indexed colors
Number-format fidelityExcel's actual builtin table (not the ECMA strings), negative-sign / accounting-parens / currency-literal compensation, elapsed [h]:mm:ss, minute-adjacent mm, date1904 shift
ErrorsFriendly, code-carrying errors: PASSWORD_PROTECTED, LEGACY_FORMAT (legacy .xls), CORRUPT, UNSUPPORTED, WASM, UNKNOWN

Install ​

bash
pnpm add @marcusok/excel-preview

That single install brings everything: the parsing engine (modern-xlsx) is bundled into this package's dist at build time and the WASM binary ships under its own exports map. See Quick Start.

Quick example ​

ts
import { createPreview } from "@marcusok/excel-preview";

const preview = createPreview(el, {
  source: file, // File | Blob | Uint8Array | ArrayBuffer
  onParsed: (info) => console.log(info.sheetNames, info.duration),
  onError: (e) => console.error(e.code, e.message),
});

// setSheet()/getSheetNames() are no-ops until the parse resolves;
// destroy() takes effect immediately (cancels the pending render and callbacks)
preview.setSheet("Sheet2"); // by name or index
preview.destroy(); // unmount + free resources

A low-level API — parseWorkbookBytes(bytes, { password }) — returns a plain, structured-clone-safe data model (PreviewWorkbook) for React/Vue wrappers, SSR, or custom renderers.

Not in scope (v1) ​

  • Editing / formula recalculation — formula cells render their cached values (the industry convention; a formula saved without a cached value renders empty).
  • Legacy .xls (BIFF8) and .ods — rejected with a clear message.
  • Charts, images, shapes, conditional formatting, rich-text runs (rendered as plain concatenated text), and external hyperlink URLs (the engine's read path drops them; the link styling still shows).