npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@file-viewer/docx

v0.3.27

Published

Pure HTML DOCX renderer fork for browser-based Word previews.

Readme

npm version

@file-viewer/docx

Pure HTML DOCX renderer for browser-based Word previews. This package preserves the final non-canvas docx-viewer line as a stable maintenance track, converting WordprocessingML into HTML while keeping semantic structure, page layout, headers/footers, numbering, fields, tables, images and common DrawingML content as closely as browser layout allows.

This fork includes production hardening for large Word documents: optional Worker parsing with a main-thread fallback, asynchronous rendering yields, Word-saved pagination support, dynamic overflow pagination and layout telemetry.

Installation

npm install @file-viewer/docx

For the standalone browser build, ship these files together and keep the Worker URL same-origin:

dist/docx-preview.js
dist/docx-preview.worker.js
dist/jszip.min.js

Basic usage

<script src="dist/jszip.min.js"></script>
<script src="dist/docx-preview.js"></script>
<div id="container"></div>
<script>
const options = {
  useWorker: true,
  workerUrl: "dist/docx-preview.worker.js",
  workerJsZipUrl: "dist/jszip.min.js",
  awaitLayout: true,
  strictWordCompatibility: true,
  ignoreLastRenderedPageBreak: false,
  preserveComplexFieldResults: true,
  updatePageReferences: false,
  hideWebHiddenContent: false,
  externalLinkPolicy: "allow",
  progress: ev => console.log(ev.phase, ev.current, ev.total, ev.message)
};

docx.renderAsync(fileOrArrayBuffer, document.getElementById("container"), null, options)
  .then(() => console.log("docx: finished"));
</script>

ES module usage:

import { renderAsync } from "@file-viewer/docx";

await renderAsync(fileOrArrayBuffer, container, null, {
  useWorker: true,
  workerUrl: new URL("@file-viewer/docx/dist/docx-preview.worker.js", import.meta.url).toString(),
  workerJsZipUrl: new URL("@file-viewer/docx/dist/jszip.min.js", import.meta.url).toString(),
  awaitLayout: true
});

API

renderAsync(
  document: Blob | ArrayBuffer | Uint8Array,
  bodyContainer: HTMLElement,
  styleContainer?: HTMLElement,
  options?: Partial<Options>
): Promise<WordDocument>

parseAsync(
  document: Blob | ArrayBuffer | Uint8Array,
  options?: Partial<Options>
): Promise<WordDocument>

parseAsyncInWorker(
  document: Blob | ArrayBuffer | Uint8Array,
  options?: Partial<Options>
): Promise<WordDocument>

renderDocument(
  wordDocument: WordDocument,
  options?: Partial<Options>
): Promise<Node[]>

awaitRenderedLayout(
  container: HTMLElement,
  options?: Partial<Options>
): Promise<LayoutSnapshot>

collectLayoutSnapshot(
  container: HTMLElement,
  options?: Partial<Options>
): LayoutSnapshot

Important options

{
  className: "docx",
  inWrapper: true,
  hideWrapperOnPrint: false,
  ignoreWidth: false,
  ignoreHeight: false,
  ignoreFonts: false,
  breakPages: true,

  // Appearance
  darkMode: false,

  // Word fidelity / pagination
  ignoreLastRenderedPageBreak: false,
  strictWordCompatibility: true,
  paginationTolerance: 2,
  maxDynamicPaginationPasses: 1000,
  awaitLayout: true,

  // Worker and responsiveness
  useWorker: true,
  workerUrl: "dist/docx-preview.worker.js",
  workerJsZipUrl: "dist/jszip.min.js",
  workerFallback: true,
  workerTimeout: 120000,
  renderPageBatchSize: 2,
  renderYieldEveryMs: 16,
  progress: ev => void,

  // Field and print-layout compatibility
  preserveComplexFieldResults: true,
  updatePageReferences: false,
  hideWebHiddenContent: false,

  // Content switches
  renderHeaders: true,
  renderFooters: true,
  renderFootnotes: true,
  renderEndnotes: true,
  renderComments: false,
  renderAltChunks: true,
  renderChanges: false,
  experimental: false,
  trimXmlDeclaration: true,
  useBase64URL: false,
  debug: false
}

Large-document rendering path

The production path is intentionally split into stages:

  1. The main thread starts a Worker using workerUrl.
  2. The Worker loads JSZip using workerJsZipUrl, unzips the package, parses XML parts, resolves relationships and serializes a compact document snapshot.
  3. The main thread restores the snapshot into WordDocument and renders HTML pages in batches, yielding to requestIdleCallback, requestAnimationFrame or setTimeout between page batches.
  4. awaitRenderedLayout waits for images and fonts, runs dynamic overflow pagination and returns a LayoutSnapshot.
  5. collectLayoutSnapshot can be used in CI or telemetry to detect overflow pages, unresolved media, page count, aggregate text length, fields and floating objects.

Batch rendering and browser yields avoid the former demo-page freeze caused by inserting very large DOM trees in one task. When Worker parsing is enabled, keep workerFallback: true unless the target Worker runtime provides every required XML API.

Pagination, headers/footers and TOC

The renderer supports:

  • explicit page breaks (w:br w:type="page");
  • Word-saved page break positions (w:lastRenderedPageBreak), enabled by default;
  • paragraph pageBreakBefore, keepNext, keepLines and widowControl hints;
  • section page size, orientation, columns, margins, headers and footers;
  • table row splitting with w:tblHeader repeat-header preservation;
  • post-render dynamic pagination for content that still overflows after structural page splitting;
  • layout-time updates for truly page-local fields such as PAGE, NUMPAGES, SECTIONPAGES and SECTION;
  • preservation of Word's stored complex-field results for TOC, PAGEREF, REF, SEQ, IF, MERGEFIELD and related fields by default, so a Word-authored table of contents keeps its cached page numbers and tab leaders instead of being recalculated incorrectly by the browser.

For Word-authored documents, keep ignoreLastRenderedPageBreak: false. Those markers preserve the positions calculated by the Word-compatible editor that last saved the document and are especially important for long tables and large Chinese technical documents. Keep preserveComplexFieldResults: true unless you intentionally want to recompute cross-reference fields; the default matches Word print-layout behavior for existing TOC/PAGEREF results.

w:webHidden is not hidden by default in this renderer because it only applies to Word's Web Layout view. In Print Layout, Word still displays the TOC tab before the page number and the cached PAGEREF result even when those runs are marked w:webHidden. Set hideWebHiddenContent: true only for an explicit Web Layout style preview.

External hyperlinks remain active by default. Set externalLinkPolicy: "block" to omit their href while preserving their text and target metadata. Document-internal bookmark links remain active in either mode.

Set darkMode: true to reproduce Word Online's "Switch Modes" dark appearance. Every color the renderer emits — text, backgrounds/highlights, borders, underlines, theme colors and DrawingML/VML shape fills and strokes — is converted with the same lightness-inversion algorithm Microsoft uses for this exact purpose (see roosterjs's getDarkColor): the color is converted to CIE Lab, only the lightness (L) channel is inverted around a fixed base value (the L of #333333), and hue/chroma (a/b) are left untouched. That guarantees every color stays legible on a dark page — pure black becomes pure white, pure white becomes #333333, and an arbitrary color such as rgb(99, 36, 35) keeps its reddish hue while becoming light enough to read — without the hue-flipping side effects of a naive per-channel RGB invert. The toggle is purely a rendering-time CSS transform — it does not change the parsed document model — and every transformed color is wrapped in a CSS variable so that @media print can restore the exact original light-mode value; print output always renders with Word's normal light appearance regardless of darkMode.

Header/footer selection follows the WordprocessingML print-layout rules: first header/footer references are used only when the section has w:titlePg, and even references are used only when document settings contain w:evenAndOddHeaders; otherwise the default/odd header is used. This prevents even-page empty headers from hiding the normal header in documents that merely contain unused even header references.

Complex-script formatting is kept script-aware. w:iCs and w:bCs now affect RTL/complex-script spans, but they are not applied to East Asian TOC text; this avoids turning Chinese TOC level 3 entries italic when the DOCX only specified complex-script italics.

Regression fixture

tests/regression/database-design.docx is a large Chinese database-design document used to validate production behavior. The fixture has a large word/document.xml, thousands of paragraphs, many tables, TOC/PAGEREF fields, saved page breaks and multiple header/footer references.

Open this page in a local server to run the browser regression:

tests/regression/database-design-render.html

The Node regression can be run with:

node tests/regression/check-database-design.cjs

It renders tests/regression/database-design.docx and asserts that TOC entries keep Word's cached page numbers such as 引言 5, 各系统与数据库对应关系 6, 表间关系 16 and 数据恢复策略 103 instead of collapsing to page 1. It also verifies that w:tab right-tab leaders are emitted as measurable docx-tab-stop elements with dotted leader styling, so TOC and figure-list page-number dot leaders remain visible. The regression also checks print-layout TOC hyperlink styling: TOC hyperlinks keep their anchors, but nested Hyperlink character-style runs are forced to inherit the paragraph color and text decoration, matching Word's black print-layout TOC rather than a browser-blue link. A successful browser run updates the fixed status banner with page count, overflow pages, unresolved media and text length.

Build

This repository includes a lightweight build script that emits synchronized, minified CommonJS, ESM and Worker bundles:

npm run build

Run the complete build and semantic regression suite with:

npm run verify

Browser-focused checks remain available through npm run e2e, npm run e2e:chart and npm run e2e:header-guard.

Local demo

Serve the repository over HTTP and open the root page:

python3 -m http.server 8000

The demo is self-contained for normal DOCX files, includes sample documents, page navigation, light/dark rendering and an options panel. TIFF support is loaded only when a document actually contains TIFF media.

Notes on fidelity

The renderer follows OOXML semantics and uses browser-native layout. It is designed for production preview and regression testing, not for claiming byte-for-byte or pixel-perfect identity with Microsoft Word's private layout engine. Use LayoutSnapshot and the supplied regression fixture to monitor the remaining differences that depend on browser fonts, font fallback and shaping engines.

Numbering suffix and heading/list numbering

OOXML separates numbering text from the suffix after the numbering symbol. w:lvlText contains the numbering pattern, w:numFmt defines the format used to expand %1, %2, etc., and w:suff defines the content inserted between the generated number and paragraph text. When w:suff is omitted, OOXML treats it as tab. The renderer emits this suffix as a valid CSS unicode separator token, not as escaped visible text, so headings/lists no longer show literal \9 or \a0 after the numbering.

Run the focused regression with:

node tests/regression/check-numbering-suffix.cjs

The large database-design regression uses the cached rendered snapshot by default to avoid spending minutes rebuilding the full document in constrained CI. Set DOCXJS_RERENDER_REGRESSION=1 when you need to regenerate it from the DOCX.