← All formats

DOCX

Render DOCX files in the browser with JavaScript.

The examples below render a sample Word document (.docx) in your browser and show the corresponding TypeScript.

Story · Demo

Single viewer with navigation

Hand DocxViewer a canvas and it manages parsing, page layout and the current page. Step through with the built-in nextPage() / prevPage().

sample-1.docx live · WASM
import { DocxViewer } from '@silurus/ooxml/docx';

// The built-in viewer tracks the current page for you.
const viewer = new DocxViewer(canvas, { width: 960, useGoogleFonts: true });
await viewer.load('/sample.docx');

nextBtn.addEventListener('click', () => viewer.nextPage());
prevBtn.addEventListener('click', () => viewer.prevPage());

Story · DocxScrollViewer

Scroll through every page

Use DocxScrollViewer for a virtualized, ready-made long-document reader. For a custom scrolling UI, combine DocxViewer with DocxDocument and let your application own the container and navigation.

sample-1.docx live · WASM
import { DocxScrollViewer } from '@silurus/ooxml/docx';

// The built-in scroll viewer virtualizes a long document for you.
const scroller = document.querySelector('#scroller') as HTMLElement;
const viewer = new DocxScrollViewer(scroller, {
  enableTextSelection: true,
  useGoogleFonts: true,
});

await viewer.load('/sample.docx');

window.addEventListener('pagehide', (event) => {
  if (event.persisted) return;
  viewer.destroy();
});

Large documents

Show the opening pages before pagination finishes

Set progressiveLayout: true on DocxViewer, DocxScrollViewer, or DocxDocument.load(). For a large document, load() resolves when the opening pages are paintable while the same paginator continues in the background. This works in both render modes; worker mode also keeps the remaining layout and paint work off the UI thread.

Totals are provisional

While layoutComplete is false, pageCount means pages available so far, not the final total. Do not snapshot it when load() resolves.

Subscribe or await

Use onPageChange or onVisiblePageChange for live navigation UI. Await waitUntilLayoutComplete() before printing, exporting, or relying on the authoritative total.

Page fields converge too

In-document NUMPAGES fields are repainted with the authoritative value after pagination converges. Application-owned “Page X of Y” UI should use the callback’s layoutComplete flag to mark provisional totals.

import { DocxScrollViewer } from '@silurus/ooxml/docx';

const container = document.querySelector('#document') as HTMLElement;
const pager = document.querySelector('#pager') as HTMLElement;

const viewer = new DocxScrollViewer(container, {
  progressiveLayout: true,
  mode: 'worker',
  onVisiblePageChange(pageIndex, availablePages, layoutComplete) {
    pager.textContent =
      `Page ${pageIndex + 1} of ${availablePages}${layoutComplete ? '' : '…'}`;
    pager.setAttribute('aria-busy', String(!layoutComplete));
  },
});

await viewer.load('/document.docx');

// Print, export, and final-page-count UI need the converged layout.
await viewer.waitUntilLayoutComplete();
console.log('Final page count:', viewer.pageCount);

Story · ThumbnailGrid

Page thumbnails

The same engine renders pages at any size. Lay them out in a grid at thumbnail width for a quick overview, with click-to-navigate.

sample-1.docx live · WASM
import { DocxDocument } from '@silurus/ooxml/docx';

// Render each page small, wire up navigation.
const document = await DocxDocument.load('/sample.docx');

for (let i = 0; i < document.pageCount; i++) {
  const thumb = document.createElement('canvas');
  thumb.addEventListener('click', () => open(i));
  grid.appendChild(thumb);
  await document.renderPage(thumb, i, { width: 320 });
}

Story · MasterDetail

Thumbnail rail + large preview

Combine both: a DocxDocument for the thumbnail rail and a DocxViewer for the detail pane. Click a thumbnail to jump the preview with goToPage().

sample-1.docx live · WASM
import { DocxDocument, DocxViewer } from '@silurus/ooxml/docx';

// Parse once, then lend the loaded engine to every view that needs it.
const document = await DocxDocument.load('/sample.docx');

// A large preview on the right borrows the engine and cannot acquire another source.
const viewer = DocxViewer.fromDocument(detailCanvas, document, {
  width: 960,
  enableTextSelection: true,
});
await viewer.goToPage(0);

// The thumbnail rail on the left renders from that same engine.
for (let i = 0; i < document.pageCount; i++) {
  const thumb = document.createElement('canvas');
  thumb.addEventListener('click', () => viewer.goToPage(i));  // jump the preview
  rail.appendChild(thumb);
  await document.renderPage(thumb, i, { width: 200 });
}

window.addEventListener('pagehide', (event) => {
  if (event.persisted) return;
  viewer.destroy();
  document.destroy(); // borrowed engines remain caller-owned
});

Live · Built-in comment UI

Comments in context, rendered by ScrollViewer.

Turn on the built-in read-only UI when a conventional page-side comment margin is enough. The Viewer owns positioning, highlights, markers, zoom, and scrolling. Comment UI implementation guide →
DOCX ScrollViewer sample-1.docx

Loading sample-1.docx…

Comments appear beside the second page.

import { DocxScrollViewer } from '@silurus/ooxml/docx';

const viewer = new DocxScrollViewer(container, {
  comments: true,
});

await viewer.load(source);