GitHub

DOCX Viewer

A docx-preview-backed Word viewer with paginated rendering, zoom, fit-to-width, and download.

The DOCX Viewer renders a Word document with docx-preview directly — faithful, paginated pages (page size, margins, headers and footers), continuous scroll, zoom, fit-to-width, and download. It mirrors the PDF Viewer so the two are drop-in siblings.

It is built without manual page bookkeeping: the document blob loads through React's use() with Suspense, docx-preview lays out the pages once, and off-screen pages are skipped by the browser via CSS content-visibility — so a long document only lays out and paints the pages near the viewport.

Installation

pnpm dlx shadcn@latest add @retab/docx-viewer

This pulls in docx-preview (and its single dependency, jszip). Everything is resolved from the installed package by your bundler — there is no runtime CDN call.

Usage

import { DocxViewer } from "@/components/ui/docx-viewer"
 
export function Example() {
  return <DocxViewer src="/document.docx" className="h-[600px]" />
}

Performance

docx-preview renders the document to the DOM (not a canvas), which is already cheap. On top of that, each page section gets content-visibility: auto with a matching contain-intrinsic-size, so the browser skips layout and paint for pages outside the viewport while keeping the scrollbar stable. Zoom is applied with CSS zoom on the laid-out pages, so changing scale never re-parses the document.

Props

PropTypeDescription
srcstringURL of the .docx (same-origin or CORS-enabled).
scalenumberOptional fixed zoom; omit to fit page width to the container.
toolbarbooleanShow the zoom/download toolbar. Defaults to true.
downloadFileNamestringFilename for the download button.
onVisiblePageChange(page: number) => voidFired with the 1-based page nearest the top of the viewport.
onScrollProgressChange(progress: number) => voidFired with scroll progress in [0, 1].
headerReactNodeStrip rendered directly below the toolbar.
asideReactNodeLeft rail rendered alongside the scrolling pages.
barebooleanDrop the outer border/background so the viewer fills its container.
classNamestringOptional class on the viewer shell.