react-docx is a React-first DOCX viewer and editor built as a monorepo.
The main package is @extend-ai/react-docx, which gives you:
- A simple read-only viewer for rendering
.docxfiles or prebuilt document models - A richer editor/controller API for building custom DOCX editing UIs
- Pagination, page layout, theme, tracked-change, form-field, and thumbnail hooks
- Configurable page surface and inter-page background colors
- A dark read-only night-reader mode that inverts document content while preserving image hues
- Lower-level OOXML, model, layout, and serialization exports for custom pipelines
For application code, you usually want the main package:
pnpm add @extend-ai/react-docx react react-domreact and react-dom are peer dependencies.
The public package exports two useful levels of API:
-
ReactDocxViewerA lightweight read-only viewer when you just want to render a document. -
useDocxEditor+DocxEditorViewerThe full controller/view split for editable or highly customized experiences.
It also re-exports the internal document/model/layout/serializer packages, so you can work below the UI layer when needed.
Use ReactDocxViewer when you want the smallest integration surface.
import * as React from "react";
import { ReactDocxViewer } from "@extend-ai/react-docx";
export function ReadOnlyDocxExample() {
const [file, setFile] = React.useState<ArrayBuffer | undefined>();
return (
<div style={{ display: "grid", gap: 12 }}>
<input
type="file"
accept=".docx"
onChange={async (event) => {
const nextFile = event.target.files?.[0];
setFile(nextFile ? await nextFile.arrayBuffer() : undefined);
}}
/>
<ReactDocxViewer
file={file}
emptyState="Choose a DOCX file to preview."
/>
</div>
);
}You can also pass a model instead of a raw .docx file if you already have a normalized document model.
Use useDocxEditor when you want import/export controls, document theme state, selection-aware editing commands, pagination state, and a customizable document canvas.
import * as React from "react";
import {
DocxEditorViewer,
useDocxDocumentTheme,
useDocxEditor,
useDocxPagination,
} from "@extend-ai/react-docx";
export function EditorExample() {
const editor = useDocxEditor({
initialDocumentTheme: "light",
initialStatus: "Ready",
});
const { documentTheme, toggleDocumentTheme } = useDocxDocumentTheme(editor);
const { pagination } = useDocxPagination(editor);
return (
<div style={{ display: "grid", gap: 12 }}>
<div style={{ display: "flex", gap: 8, alignItems: "center" }}>
<input
type="file"
accept=".docx"
onChange={(event) => {
const file = event.target.files?.[0];
if (file) {
void editor.importDocxFile(file);
}
}}
/>
<button type="button" onClick={() => editor.exportDocx()}>
Export DOCX
</button>
<button type="button" onClick={() => toggleDocumentTheme()}>
Theme: {documentTheme}
</button>
<span>
Page {pagination.currentPage} / {pagination.totalPages}
</span>
<span>{editor.status}</span>
</div>
<DocxEditorViewer
editor={editor}
mode="edit"
pageBackgroundColor="#ffffff"
pageGapBackgroundColor="transparent"
/>
</div>
);
}The library can expose page thumbnails from the mounted viewer surfaces so you can build your own page strip, mini-map, or navigation UI.
import * as React from "react";
import {
DocxEditorViewer,
useDocxEditor,
useDocxPageThumbnails,
} from "@extend-ai/react-docx";
export function ThumbnailExample() {
const editor = useDocxEditor();
const { thumbnails } = useDocxPageThumbnails(editor, {
maxWidthPx: 160,
maxHeightPx: 220,
pixelRatio: 2,
});
return (
<div style={{ display: "grid", gridTemplateColumns: "180px 1fr", gap: 16 }}>
<div style={{ display: "grid", gap: 12, alignContent: "start" }}>
{thumbnails.map((thumbnail) => (
<canvas
key={thumbnail.pageIndex}
ref={thumbnail.canvasRef}
width={thumbnail.pixelWidthPx}
height={thumbnail.pixelHeightPx}
style={{
width: thumbnail.widthPx,
height: thumbnail.heightPx,
border: "1px solid #ddd",
}}
/>
))}
</div>
<DocxEditorViewer
editor={editor}
pageVirtualization={{ enabled: false }}
/>
</div>
);
}Notes:
- Thumbnails are produced from mounted page DOM.
- Thumbnail sizing is bounded by
maxWidthPxandmaxHeightPx, so downstream UIs can bias toward portrait thumbnail rails. - If page virtualization is enabled, offscreen pages can report
status: "unavailable". - For a full thumbnail rail, disable virtualization or manage the visible page range yourself.
@extend-ai/react-docx exports several hooks for wiring custom UI around the viewer:
useDocxDocumentTheme(editor)for light/dark document modeuseDocxPagination(editor)for current page and total page countuseDocxPageLayout(editor)for page size, margins, columns, and viewport defaultsuseDocxPageThumbnails(editor, options)for rendering page previews into your own canvasesuseDocxParagraphStyles(editor)for available paragraph styles and style selectionuseDocxLineSpacing(editor)for selected line spacing stateuseDocxBorders(editor)for paragraph/table border presetsuseDocxFormFields(editor)for DOCX form-field state and updatesuseDocxTrackChanges(editor)for tracked-change UI stateuseDocxImageWrapMenu(editor)for image wrapping controls
The package also re-exports the lower-level internals used by the viewer:
- OOXML parsing from
@extend-ai/react-docx-ooxml-core - Document model helpers from
@extend-ai/react-docx-doc-model - Editing operations from
@extend-ai/react-docx-editor-ops - Layout primitives from
@extend-ai/react-docx-layout-engine - Layout/core snapshot helpers from
@extend-ai/react-docx-layout-core - DOCX serialization from
@extend-ai/react-docx-serializer
This means you can build your own pipeline, for example:
import {
buildDocModel,
parseDocx,
serializeDocx,
} from "@extend-ai/react-docx";
const pkg = await parseDocx(arrayBuffer);
const model = buildDocModel(pkg);
const output = serializeDocx(model, pkg);DocxEditorViewersupportsmode="edit"andmode="read-only".pageBackgroundColorcontrols the page surface color.pageGapBackgroundColorcontrols the area between pages and defaults to transparent.- The built-in dark page surface uses Tailwind
neutral-950by default. - In read-only dark document mode, the viewer uses an inversion-based night reader path so document content inverts cleanly while images keep their hues.
packages/react-viewer: the main published React package,@extend-ai/react-docxpackages/ooxml-core: OOXML package parsing and part graph handlingpackages/doc-model: normalized document modelpackages/editor-ops: document editing operationspackages/layout-engine: pagination and layoutpackages/layout-core: layout helpers and snapshot-oriented utilitiespackages/serializer: model-to-DOCX serializationapps/playground: local playground for manual QA and feature developmenttests/unit: unit coveragetests/visual: visual regression coverage
Install dependencies:
pnpm installRun the playground:
pnpm devBuild all packages:
pnpm buildTypecheck packages and the playground:
pnpm typecheckRun unit tests:
pnpm test:unitRun visual tests:
pnpm test:visualRun the DOCX-vs-LibreOffice comparison flow:
pnpm test:docx-vs-libreofficeChangesets are used for release management:
pnpm changeset
pnpm version-packages
pnpm build
pnpm publish-packages