Story · Demo
The full workbook viewer
Unlike slides and pages, a spreadsheet is one continuous grid — so XlsxViewer owns its own canvas, sheet-tab bar and zoom slider. Switch sheets along the bottom, click-drag to select a range, press Ctrl/Cmd+C to copy it as TSV, zoom with the slider or Ctrl/Cmd+wheel · trackpad pinch (10–400%), and drag a column/row header border to resize it. Resizing and zooming are view-only — they change what you see, never the loaded file.
import { XlsxViewer } from '@silurus/ooxml/xlsx';
// XlsxViewer owns its canvas, sheet-tab bar and zoom slider — hand it a
// container element (not a canvas). Click-drag selects a range; Ctrl/Cmd+C
// copies it as TSV.
const container = document.getElementById('sheet') as HTMLElement;
const viewer = new XlsxViewer(container, { showZoomSlider: true });
await viewer.load('/sample.xlsx'); Workbook · Multi-window
One workbook. Many windows.
Parse the file once in the parent page, then open any sheet in its own browser window. Every window gets its own visible scrollbars, zoom and selection state while the workbook archive, worksheet cache and render worker remain shared. Open two or more sheets to compare them side by side.
import { XlsxSheetViewer, XlsxWorkbook } from '@silurus/ooxml/xlsx';
const workbook = await XlsxWorkbook.load('/sample.xlsx');
const viewers = new Map<Window, XlsxSheetViewer>();
async function openSheetInWindow(sheetIndex: number): Promise<void> {
// Call this function directly from a click handler so popup blockers allow it.
const popup = window.open('', '_blank', 'popup,width=1100,height=720,resizable=yes');
if (!popup) throw new Error('The browser blocked the popup');
const canvas = popup.document.createElement('canvas');
canvas.style.cssText = 'display:block;width:100%;height:100%';
popup.document.body.style.margin = '0';
popup.document.body.appendChild(canvas);
const viewer = XlsxSheetViewer.fromWorkbook(canvas, workbook);
viewers.set(popup, viewer);
popup.addEventListener('pagehide', () => {
viewer.destroy();
viewers.delete(popup);
}, { once: true });
await viewer.goToSheet(sheetIndex);
}
// Example: openSheetInWindow(1) from a sheet button's click handler.
window.addEventListener('pagehide', () => {
viewers.forEach((viewer, popup) => {
viewer.destroy();
popup.close();
});
workbook.destroy();
}, { once: true }); API reference
Options & methods
Every public option and method, straight from the source. Types omitted for brevity are exported from the package — your editor will autocomplete the rest.
Choose one loading mode
Normal case: construct the Viewer, then call viewer.load(source).
The Viewer owns the parsed engine, supports replacement loads, and destroys it during teardown.
Shared-engine case: load XlsxWorkbook once and create
each view with fromWorkbook(). This is mutually exclusive with Viewer loading:
load() is unavailable, and the caller must destroy the borrowed engine after every
view has been destroyed.
Loading ownership and execution mode are separate choices. Both loading modes support both execution modes:
| Loading ownership | mode: 'main' | mode: 'worker' |
|---|---|---|
| Viewer-owned | Pass mode: 'main' to the Viewer, then call load(source). | Pass mode: 'worker' to the Viewer, then call load(source). |
| Shared engine | XlsxWorkbook.load(source, { mode: 'main' }), then fromWorkbook(). | XlsxWorkbook.load(source, { mode: 'worker' }), then fromWorkbook(). |
On the shared-engine path, the engine's mode is authoritative: fromWorkbook()
deliberately does not accept a mode and cannot switch an already-loaded engine. Here,
main means parsing in a Worker and rendering on the main thread; worker
means both parsing and rendering in a Worker. Worker rendering requires Worker and
OffscreenCanvas support and may have format-specific feature limitations.
Error handling
Typed container, resource-limit and decoded-image failures expose stable fields. Other failures remain ordinary JavaScript errors. The callback and Promise delivery rules are part of the public contract.
Open the error reference →Selection context for AI and external tools
Viewer selection remains read-only UI state. getSelectionContext() turns the
current focus into a detached, resource-bounded snapshot; every format can notify you through
onSelectionContextChange when that snapshot may have changed.
XlsxViewer
Full workbook viewer. Takes a container <div> (not a canvas) — it manages its own canvas, sheet-tab bar and zoom slider. Drag-to-resize columns/rows and zoom are view-only: they change the on-screen view only and never modify the loaded file.
new XlsxViewer(container: HTMLElement, options?: XlsxViewerOptions) Options
| Name | Type | Default | Description |
|---|---|---|---|
| Properties | |||
cellScale | number | 1 | Scale factor for cell/header dimensions (0.5 = half size). |
showZoomSlider | boolean | true | Show the Excel-style zoom slider at the end of the tab bar. Zooming (slider, Ctrl/⌘+wheel, trackpad pinch) is view-only. |
zoomMin / zoomMax | number | 0.1 / 4 | Zoom slider bounds as scale factors (10%–400%). |
resizable | boolean | true | Allow resizing columns/rows by dragging header borders. View-only — it changes the on-screen view only and never modifies the loaded file. Set false to disable. |
showScrollbars | boolean | true | Show native worksheet scrollbars. Set false only when the host supplies another viewport navigation UI. |
selectionColor | string | '#1a73e8' | Accent color for the cell-selection rectangle (any CSS color). The fill is the same color at 8% opacity. |
enableElementSelection | boolean | false | Enable read-only chart, picture, and shape selection with a non-editable outline and element context, without changing the underlying cell selection. |
findHighlightColors | { match?: string; active?: string } | yellow / orange | CSS backgrounds for ordinary and active find matches. Values are applied verbatim; use an alpha color to keep the canvas text visible through the overlay. |
hiddenSheetMode | 'show' | 'skip' | 'dim' | 'show' | How hidden / very-hidden sheets (<sheet state>, §18.2.19) appear in the tab bar. show renders a tab like any other; skip hides the tab (display:none) and makes sequential navigation jump over it; dim renders the tab at reduced opacity. Mirrors pptx hiddenSlideMode. |
useGoogleFonts | boolean | false | Load metric-compatible webfonts and non-Latin script fallbacks (Noto Arabic / CJK KR·SC·TC·JP / Cyrillic / Hebrew / Thai / Devanagari) from Google Fonts so layout matches Office and non-Latin text never falls back to tofu. Off by default for privacy. |
password | string | undefined | Password for an Agile-encrypted OOXML file. Available on self-loading Viewer constructors and headless load(); borrowed fromDocument(), fromPresentation(), and fromWorkbook() factories omit load-only options because their engine is already loaded. |
maxZipEntryBytes | number | resource policy default | Deprecated compatibility alias for resourceLimits.maxArchiveEntryBytes. It is scheduled for removal in a future breaking release; new code should use resourceLimits. Existing positive values retain their per-entry meaning; zero / negative values fall back to the standard default. Migration → |
resourceLimits | OoxmlResourceLimits | 128 MiB per entry / 256 MiB distinct total / 4,096 entries | Shared DOCX/XLSX/PPTX package budgets. maxArchiveEntryBytes caps each package part; maxTotalInflatedBytes counts the largest amount read from every distinct part without charging repeat reads twice; maxArchiveEntries bounds central-directory entries before ZIP index allocation. Supply positive safe integers, or null to disable one configurable budget (internal hard ceilings remain). Violations reject with OoxmlResourceLimitError. These deterministic counters reduce OOM risk but do not measure or guarantee peak memory. Error fields → |
debug | boolean | false | Print one content-free, Ratatui-inspired resource report when the measured load or Node session finishes or fails. Browser DevTools use typography-only %c styling to keep Unicode borders and gauges aligned without changing foreground or background colours; Node and Worker consoles receive one plain argument. Use onResourceMetrics instead for production collection. |
math | MathRenderer | undefined | Opt-in OMML equation engine (MathJax + STIX Two Math, ~3 MB). Import it from the separate @silurus/ooxml/math entry — import { math } from "@silurus/ooxml/math" — and pass it to render equations. Omit it and equations are skipped, and the engine is left out of your build. When passed, the engine ships as a standalone asset fetched lazily the first time a document contains an equation. |
mode | 'main' | 'worker' | 'main' | 'main' renders on the main thread (default). 'worker' renders the viewer off the main thread and paints transferred ImageBitmaps, improving UI responsiveness and containing parser/renderer state away from Window. It is not a separate process or a strict memory sandbox and cannot guarantee recovery from every browser-level OOM. Scroll, tabs, zoom, selection and find remain available; equations require 'main'. Requires Worker + OffscreenCanvas, and frame transfer can add latency. |
enableHyperlinks | boolean | true | Master switch for hyperlink interactivity. Set false to disable it entirely: no hit-testing, no pointer cursor over links, no default navigation, and onHyperlinkClick is never called. Links still render exactly as authored but are inert, like plain text. |
| Event handlers | |||
onResourceMetrics | (metrics: OoxmlResourceMetrics) => void | — | Receives the content-free initial-load report used by the debug card, without enabling console output. It reports the configured public policy, timing checkpoints, format/mode, success or typed failure discriminants, source bytes, and observed archive counters when available. It does not wait for a Viewer's first paint. On success, call getResourceMetrics() on the engine or Viewer for a fresh snapshot after lazy package work. Callback exceptions never change load results. |
onScaleChange | (scale: number) => void | — | Called when the zoom factor changes (setScale / fitWidth / fitPage / zoomIn / zoomOut), with the clamped factor (1 = 100%). |
onHyperlinkClick | (target: HyperlinkTarget) => void | — | Called when a hyperlink is clicked. target is { kind: 'external', url } or { kind: 'internal', ref, slideIndex? }. When supplied, the callback fully owns the click (the default external-open / internal-navigation is not run). External URLs are scheme-sanitized (http / https / mailto / tel only); internal targets resolve to a docx bookmark / pptx slide jump / xlsx defined name or cell. |
onReady | (sheetNames: string[]) => void | — | Called once the workbook is parsed. |
onSheetChange | (index: number, total: number) => void | — | Called when the active sheet changes; total is the sheet count. Read the name via sheetNames[index]. |
onSelectionStateChange | (sel: XlsxSelectionState | null) => void | — | Called only when canonical selection state changes; geometry, ActiveCell, extension anchor, and multiple areas remain distinct. |
onSelectionContextChange | (context: XlsxSelectionContext | null) => void | — | Receive bounded detached range or element context for read-only AI/MCP handoff. This callback does not enable element hit-testing; rapid changes are coalesced per animation frame. |
onContextMenu | (event: ViewerContextMenuEvent<XlsxSelectionContext>) => void | — | Called synchronously for the native contextmenu event. Use originalEvent.preventDefault() before returning to replace the browser menu; getContext() starts one memoized target lookup on first call. Omit the callback to keep native browser behavior unchanged. |
onViewportChange | (offset: XlsxViewportOffset) => void | — | Called with the clamped logical CSS-pixel offset after the active viewport moves. Horizontal x is measured from column A in both LTR and RTL sheets. |
onError | (err: Error) => void | — | Receives Viewer-managed failures that have no directly awaitable result, such as virtualized rendering or embedded-media playback. load(), navigation, and other awaitable operations reject their own Promise whether or not this callback is supplied; the same failure is never delivered twice. Background failures are logged with console.error when the callback is omitted. Narrow stable cases with OoxmlError, OoxmlResourceLimitError or OoxmlDecodedImageLimitError; other failures remain Error values and message text is not a stable discriminator. Error reference → |
Methods
static fromWorkbook(container, workbook, options?): Omit<XlsxViewer, "load"> | Synchronously create a full Workbook Viewer that borrows one loaded workbook and starts its initial sheet display. |
load(source: string | ArrayBuffer): Promise<void> | Load a Viewer-owned workbook and render the first sheet. |
goToSheet(index: number): Promise<void> | Show a specific sheet (0-indexed, clamped). |
nextSheet(): Promise<void> | Advance one sheet. |
prevSheet(): Promise<void> | Go back one sheet. |
get sheetIndex(): number | Current sheet index. |
get sheetCount(): number | Total sheets (0 until loaded). |
get sheetNames(): string[] | Names of all sheets. |
get selectionState(): XlsxSelectionState | null | Detached canonical state for the current selection. |
setSelection(input: string | XlsxSelectionState | null): void | Set one A1 area, a complete canonical selection state, or clear the selection. A1 endpoint order does not encode ActiveCell. |
getSelectionContext(options?: { maxCells?: number; maxTextCharacters?: number }): XlsxSelectionContext | null | Return bounded { kind: "range" } cell content or { kind: "element" } clicked-object context for read-only AI/MCP handoff. |
copySelection(): Promise<XlsxCopyResult> | Copy a bounded TSV and report copied, resource-limit, unsupported-multiple-area, or Clipboard API outcomes. |
getViewportOffset(): XlsxViewportOffset | Return the active sheet viewport offset in logical CSS pixels. |
setViewportOffset(offset: XlsxViewportOffset): Promise<void> | Move the active viewport to a finite offset, clamped to the used scroll extent. |
scrollToCell(ref: string, options?: XlsxScrollToCellOptions): Promise<void> | Scroll a cell reference into view, optionally aligning it to the start, center, end, or nearest edge. |
relayout(): Promise<void> | Re-read the viewport box, clamp the current offset, and render again after an external layout change. |
getScale(): number | The current zoom factor (1 = 100%). |
setScale(scale: number): void | Set the zoom factor (1 = 100%), clamped to [zoomMin, zoomMax] and snapped to whole percent; re-renders and fires onScaleChange when it changes. View-only. |
fitWidth(): void | Fit the used data range WIDTH (row header + used columns) to the canvas area (routes through setScale). Defers when unloaded / unlaid-out. |
fitPage(): void | Fit the used data range WIDTH and HEIGHT inside the canvas area so the whole used range is visible without scrolling — takes the tighter of the two fits. Defers when unloaded / unlaid-out. |
findText(query: string, opts?: { caseSensitive?: boolean }): Promise<FindMatch<XlsxMatchLocation>[]> | Full-text search across the whole document; highlights every hit and returns them in document order. Each match carries matchIndex, the matched text, and its location. Case-insensitive by default. |
findNext(): Promise<FindMatch<XlsxMatchLocation> | null> | Move to the next match (wrap-around), navigate to it if needed, and draw it in the active-match colour. Returns the now-active match, or null when there are none. Call findText first. |
findPrev(): Promise<FindMatch<XlsxMatchLocation> | null> | Move to the previous match (wrap-around from first to last). |
clearFind(): void | Clear all highlights and reset the find state. |
setSelectionColor(color: string): void | Change the selection accent color at runtime (any CSS color). |
get hiddenSheetMode(): "show" | "skip" | "dim" | The current hidden-sheet mode. |
setHiddenSheetMode(mode: "show" | "skip" | "dim"): Promise<void> | Switch the hidden-sheet mode at runtime: restyle the tabs and re-render. Entering skip while on a hidden sheet advances to the nearest visible sheet. |
getCellAt(clientX: number, clientY: number): CellAddress | null | Hit-test a viewport coordinate to a cell address. |
get canvasElement(): HTMLCanvasElement | The underlying canvas the grid is drawn on. |
getResourceMetrics(): Promise<OoxmlResourceMetrics> | Return a fresh, content-free package-usage snapshot, including lazy archive work observed since load. Collection is always active; debug controls only console output. |
destroy(): void | Tear down the DOM subtree. Destroys a self-loaded workbook; a borrowed one remains caller-owned. |
XlsxSheetViewer
Canvas-mounted active-sheet viewport. It uses the caller canvas and the same sheet rendering, selection, find and navigation mechanics as XlsxViewer, but creates no sheet-tab/footer chrome. Native worksheet scrollbars are visible by default. DOM chrome, styles and listeners follow canvas.ownerDocument, so a parent page can mount borrowed workbook sheets into same-origin popup canvases.
new XlsxSheetViewer(canvas: HTMLCanvasElement, options?: XlsxSheetViewerOptions) Options
| Name | Type | Default | Description |
|---|---|---|---|
| Properties | |||
cellScale | number | 1 | Scale factor for cell/header dimensions (0.5 = half size). |
zoomMin / zoomMax | number | 0.1 / 4 | Zoom bounds as scale factors (10%–400%). |
resizable | boolean | true | Allow resizing columns/rows by dragging header borders. View-only. |
showScrollbars | boolean | true | Show native worksheet scrollbars. Set false only when the host supplies another viewport navigation UI. |
selectionColor | string | '#1a73e8' | Accent color for the cell-selection rectangle. |
enableElementSelection | boolean | false | Enable read-only chart, picture, and shape selection with a non-editable outline and element context. |
findHighlightColors | { match?: string; active?: string } | yellow / orange | CSS backgrounds for ordinary and active find matches. Values are applied verbatim; use an alpha color to keep the canvas text visible through the overlay. |
hiddenSheetMode | 'show' | 'skip' | 'dim' | 'show' | Controls sequential navigation and hidden-sheet visibility without adding tab chrome. |
useGoogleFonts | boolean | false | Load metric-compatible webfonts and non-Latin script fallbacks (Noto Arabic / CJK KR·SC·TC·JP / Cyrillic / Hebrew / Thai / Devanagari) from Google Fonts so layout matches Office and non-Latin text never falls back to tofu. Off by default for privacy. |
password | string | undefined | Password for an Agile-encrypted OOXML file. Available on self-loading Viewer constructors and headless load(); borrowed fromDocument(), fromPresentation(), and fromWorkbook() factories omit load-only options because their engine is already loaded. |
wasmUrl | string | URL | bundled asset | Override the URL the parser worker fetches the WebAssembly module from. By default each format resolves the *_parser_bg.wasm asset that ships next to its bundle (relative to the module URL); set this to serve it from a CDN or a self-hosted path instead (a relative value resolves against the document URL). Pointing it at a mismatched or missing file makes load() reject when the worker instantiates it. |
maxZipEntryBytes | number | resource policy default | Deprecated compatibility alias for resourceLimits.maxArchiveEntryBytes. It is scheduled for removal in a future breaking release; new code should use resourceLimits. Existing positive values retain their per-entry meaning; zero / negative values fall back to the standard default. Migration → |
resourceLimits | OoxmlResourceLimits | 128 MiB per entry / 256 MiB distinct total / 4,096 entries | Shared DOCX/XLSX/PPTX package budgets. maxArchiveEntryBytes caps each package part; maxTotalInflatedBytes counts the largest amount read from every distinct part without charging repeat reads twice; maxArchiveEntries bounds central-directory entries before ZIP index allocation. Supply positive safe integers, or null to disable one configurable budget (internal hard ceilings remain). Violations reject with OoxmlResourceLimitError. These deterministic counters reduce OOM risk but do not measure or guarantee peak memory. Error fields → |
debug | boolean | false | Print one content-free, Ratatui-inspired resource report when the measured load or Node session finishes or fails. Browser DevTools use typography-only %c styling to keep Unicode borders and gauges aligned without changing foreground or background colours; Node and Worker consoles receive one plain argument. Use onResourceMetrics instead for production collection. |
workerTimeoutMs | number | unlimited | Reject the parse if the worker does not answer within this many ms — an opt-in safety net for a wedged / crashed worker that would otherwise leave load() pending forever. Unlimited by default (a large document with heavy media can legitimately take tens of seconds). A worker that throws or fails to load already rejects immediately regardless; this only covers the "silent, never-responds" case. |
math | MathRenderer | undefined | Opt-in OMML equation engine (MathJax + STIX Two Math, ~3 MB). Import it from the separate @silurus/ooxml/math entry — import { math } from "@silurus/ooxml/math" — and pass it to render equations. Omit it and equations are skipped, and the engine is left out of your build. When passed, the engine ships as a standalone asset fetched lazily the first time a document contains an equation. |
mode | 'main' | 'worker' | 'main' | 'main' renders on the main thread (default). 'worker' renders the viewer off the main thread and paints transferred ImageBitmaps, improving UI responsiveness and containing parser/renderer state away from Window. It is not a separate process or a strict memory sandbox and cannot guarantee recovery from every browser-level OOM. Scroll, tabs, zoom, selection and find remain available; equations require 'main'. Requires Worker + OffscreenCanvas, and frame transfer can add latency. |
enableHyperlinks | boolean | true | Master switch for hyperlink interactivity. Set false to disable it entirely: no hit-testing, no pointer cursor over links, no default navigation, and onHyperlinkClick is never called. Links still render exactly as authored but are inert, like plain text. |
| Event handlers | |||
onViewportChange | (offset: XlsxViewportOffset) => void | — | Called with the clamped logical CSS-pixel offset after the active viewport moves. Horizontal x is measured from column A independently of browser RTL scrollLeft conventions. |
onResourceMetrics | (metrics: OoxmlResourceMetrics) => void | — | Receives the content-free initial-load report used by the debug card, without enabling console output. It reports the configured public policy, timing checkpoints, format/mode, success or typed failure discriminants, source bytes, and observed archive counters when available. It does not wait for a Viewer's first paint. On success, call getResourceMetrics() on the engine or Viewer for a fresh snapshot after lazy package work. Callback exceptions never change load results. |
onScaleChange | (scale: number) => void | — | Called when the zoom factor changes (setScale / fitWidth / fitPage / zoomIn / zoomOut), with the clamped factor (1 = 100%). |
onHyperlinkClick | (target: HyperlinkTarget) => void | — | Called when a hyperlink is clicked. target is { kind: 'external', url } or { kind: 'internal', ref, slideIndex? }. When supplied, the callback fully owns the click (the default external-open / internal-navigation is not run). External URLs are scheme-sanitized (http / https / mailto / tel only); internal targets resolve to a docx bookmark / pptx slide jump / xlsx defined name or cell. |
onReady | (sheetNames: string[]) => void | — | Called once the workbook is parsed. |
onSheetChange | (index: number, total: number) => void | — | Called when the active sheet changes. |
onSelectionStateChange | (sel: XlsxSelectionState | null) => void | — | Called only when canonical selection state changes. |
onSelectionContextChange | (context: XlsxSelectionContext | null) => void | — | Receive bounded detached range or element context, coalesced per animation frame. This callback does not enable element hit-testing. |
onContextMenu | (event: ViewerContextMenuEvent<XlsxSelectionContext>) => void | — | Called synchronously for the native contextmenu event. Use originalEvent.preventDefault() before returning to replace the browser menu; getContext() starts one memoized target lookup on first call. Omit the callback to keep native browser behavior unchanged. |
onError | (err: Error) => void | — | Receives Viewer-managed failures that have no directly awaitable result, such as virtualized rendering or embedded-media playback. load(), navigation, and other awaitable operations reject their own Promise whether or not this callback is supplied; the same failure is never delivered twice. Background failures are logged with console.error when the callback is omitted. Narrow stable cases with OoxmlError, OoxmlResourceLimitError or OoxmlDecodedImageLimitError; other failures remain Error values and message text is not a stable discriminator. Error reference → |
Methods
static fromWorkbook(canvas, workbook, options?): Omit<XlsxSheetViewer, "load"> | Synchronously attach a borrowed workbook without materializing a sheet. Await goToSheet(index) to render only the requested sheet. |
load(source: string | ArrayBuffer): Promise<void> | Load a Viewer-owned workbook and render its first active sheet viewport. |
goToSheet(index: number): Promise<void> | Show a specific sheet (0-indexed, clamped). |
nextSheet(): Promise<void> | Advance one sheet. |
prevSheet(): Promise<void> | Go back one sheet. |
get sheetIndex(): number | Current sheet index. |
get sheetCount(): number | Total sheets (0 until loaded). |
get sheetNames(): string[] | Names of all sheets. |
getViewportOffset(): XlsxViewportOffset | Read the logical start-anchored viewport offset in CSS pixels at the current scale. |
setViewportOffset(offset: XlsxViewportOffset): Promise<void> | Move to a finite logical offset, clamped to the used scroll extent. |
scrollToCell(ref: string, options?: { align?: "nearest" | "start" | "center" | "end" }): Promise<void> | Move the viewport to an A1 cell reference with the requested alignment. |
relayout(): Promise<void> | Re-read the canvas CSS box and repaint the current viewport. |
getScale(): number | The current zoom factor (1 = 100%). |
setScale(scale: number): void | Set the absolute zoom factor (1 = 100%), clamped to [zoomMin, zoomMax]; re-renders at the new size and fires onScaleChange when it changes. View-only. |
fitWidth(): void | Fit the content WIDTH to the host container and re-render (routes through setScale). Defers when nothing is loaded or the container is unlaid-out. |
fitPage(): void | Fit the WHOLE content (width and height) inside the container so it is visible without scrolling — takes the tighter of the two fits. Defers when unloaded / unlaid-out. |
findText(query: string, opts?: { caseSensitive?: boolean }): Promise<FindMatch<XlsxMatchLocation>[]> | Full-text search across the whole document; highlights every hit and returns them in document order. Each match carries matchIndex, the matched text, and its location. Case-insensitive by default. |
findNext(): Promise<FindMatch<XlsxMatchLocation> | null> | Move to the next match (wrap-around), navigate to it if needed, and draw it in the active-match colour. Returns the now-active match, or null when there are none. Call findText first. |
findPrev(): Promise<FindMatch<XlsxMatchLocation> | null> | Move to the previous match (wrap-around from first to last). |
clearFind(): void | Clear all highlights and reset the find state. |
get selectionState(): XlsxSelectionState | null | Detached canonical state for the current selection. |
setSelection(input: string | XlsxSelectionState | null): void | Set an A1 area, a complete canonical state, or clear the selection. |
getSelectionContext(options?: { maxCells?: number; maxTextCharacters?: number }): XlsxSelectionContext | null | Return bounded range content or clicked chart, picture, or shape context for AI/MCP use. |
copySelection(): Promise<XlsxCopyResult> | Copy bounded TSV and return an observable result. |
getCellAt(clientX: number, clientY: number): CellAddress | null | Hit-test a viewport coordinate to a cell address. |
get canvasElement(): HTMLCanvasElement | The caller-owned canvas used by the viewer. |
getResourceMetrics(): Promise<OoxmlResourceMetrics> | Return a fresh, content-free package-usage snapshot, including lazy archive work observed since load. Collection is always active; debug controls only console output. |
destroy(): void | Permanently close the viewer and restore the caller canvas. A workbook borrowed through fromWorkbook() remains caller-owned and is not destroyed. |
XlsxWorkbook
Headless engine — parse once, render any sheet viewport into any canvas you supply.
await XlsxWorkbook.load(source, options?) Options
| Name | Type | Default | Description |
|---|---|---|---|
| Properties | |||
useGoogleFonts | boolean | false | Load metric-compatible webfonts and non-Latin script fallbacks (Noto Arabic / CJK KR·SC·TC·JP / Cyrillic / Hebrew / Thai / Devanagari) from Google Fonts so layout matches Office and non-Latin text never falls back to tofu. Off by default for privacy. |
password | string | undefined | Password for an Agile-encrypted OOXML file. Available on self-loading Viewer constructors and headless load(); borrowed fromDocument(), fromPresentation(), and fromWorkbook() factories omit load-only options because their engine is already loaded. |
wasmUrl | string | URL | bundled asset | Override the URL the parser worker fetches the WebAssembly module from. By default each format resolves the *_parser_bg.wasm asset that ships next to its bundle (relative to the module URL); set this to serve it from a CDN or a self-hosted path instead (a relative value resolves against the document URL). Pointing it at a mismatched or missing file makes load() reject when the worker instantiates it. |
maxZipEntryBytes | number | resource policy default | Deprecated compatibility alias for resourceLimits.maxArchiveEntryBytes. It is scheduled for removal in a future breaking release; new code should use resourceLimits. Existing positive values retain their per-entry meaning; zero / negative values fall back to the standard default. Migration → |
resourceLimits | OoxmlResourceLimits | 128 MiB per entry / 256 MiB distinct total / 4,096 entries | Shared DOCX/XLSX/PPTX package budgets. maxArchiveEntryBytes caps each package part; maxTotalInflatedBytes counts the largest amount read from every distinct part without charging repeat reads twice; maxArchiveEntries bounds central-directory entries before ZIP index allocation. Supply positive safe integers, or null to disable one configurable budget (internal hard ceilings remain). Violations reject with OoxmlResourceLimitError. These deterministic counters reduce OOM risk but do not measure or guarantee peak memory. Error fields → |
debug | boolean | false | Print one content-free, Ratatui-inspired resource report when the measured load or Node session finishes or fails. Browser DevTools use typography-only %c styling to keep Unicode borders and gauges aligned without changing foreground or background colours; Node and Worker consoles receive one plain argument. Use onResourceMetrics instead for production collection. |
workerTimeoutMs | number | unlimited | Reject the parse if the worker does not answer within this many ms — an opt-in safety net for a wedged / crashed worker that would otherwise leave load() pending forever. Unlimited by default (a large document with heavy media can legitimately take tens of seconds). A worker that throws or fails to load already rejects immediately regardless; this only covers the "silent, never-responds" case. |
math | MathRenderer | undefined | Opt-in OMML equation engine (MathJax + STIX Two Math, ~3 MB). Import it from the separate @silurus/ooxml/math entry — import { math } from "@silurus/ooxml/math" — and pass it to render equations. Omit it and equations are skipped, and the engine is left out of your build. When passed, the engine ships as a standalone asset fetched lazily the first time a document contains an equation. |
mode | 'main' | 'worker' | 'main' | 'main' parses in a worker and renders on the main thread (default). 'worker' also renders inside the worker; the main thread only paints the returned ImageBitmap. This contains parser/renderer state and many failures away from Window, but a Worker is not a separate process or a strict memory sandbox and cannot guarantee recovery from every browser-level OOM. Requires Worker + OffscreenCanvas. Canvas-target render methods are unavailable in 'worker' mode, equations require 'main', and transferring each frame can add latency. |
| Event handlers | |||
onResourceMetrics | (metrics: OoxmlResourceMetrics) => void | — | Receives the content-free initial-load report used by the debug card, without enabling console output. It reports the configured public policy, timing checkpoints, format/mode, success or typed failure discriminants, source bytes, and observed archive counters when available. It does not wait for a Viewer's first paint. On success, call getResourceMetrics() on the engine or Viewer for a fresh snapshot after lazy package work. Callback exceptions never change load results. |
Methods
static load(source, options?): Promise<XlsxWorkbook> | Parse a workbook from a URL or ArrayBuffer. |
get sheetNames(): string[] | Names of all sheets. |
get sheetCount(): number | Total sheets. |
get mode(): "main" | "worker" | The render mode owned by this loaded workbook. |
getWorksheet(sheetIndex): Promise<Worksheet> | Parse and return one worksheet model. Saved pivot-table facts are exposed read-only via Worksheet.pivotTables, with skipped malformed parts reported through Worksheet.pivotDiagnostics; saved worksheet cells and styles remain authoritative. |
renderViewport(canvas, sheetIndex, viewport, opts?: XlsxRenderViewportOptions): Promise<void> | Render a row/col window of a sheet into the given canvas. onTextRun receives each text cell as XlsxTextRunInfo with required sheetName and A1 cellRef identity. Image bytes and decoded-image caches stay owned by the workbook. Equations in shapes render when a math engine was passed to load. Unavailable in mode: "worker" — use renderViewportToBitmap. |
renderViewportToBitmap(sheetIndex, viewport, opts: RenderViewportToBitmapOptions): Promise<ImageBitmap> | Render a sheet viewport and return it as an ImageBitmap (both modes; in worker mode the render runs off the main thread). width and height are required — a worker has no DOM element to measure. Equations in shapes are skipped in mode: "worker" (they require mode: "main"). The bitmap is caller-owned: pass it to transferFromImageBitmap (which consumes it) or call bitmap.close(). |
resolveValidationList(sheetIndex, formula1): Promise<ResolvedList> | Resolve a list-type data-validation formula1 (ECMA-376 §18.3.1.32) into the allowed values to display — inline quoted list, a range reference (each cell’s display string), or { kind: 'formula' } for named ranges. Read-only. |
getResourceMetrics(): Promise<OoxmlResourceMetrics> | Return a fresh, content-free package-usage snapshot, including lazy archive work observed since load. Collection is always active; debug controls only console output. |
destroy(): void | Release the worker. |