# DOCX Editor — full documentation This file contains the complete docs for @eigenpal/docx-js-editor, concatenated and stripped of HTML/frontmatter so an LLM can ingest it in one shot. Source of truth: https://www.docx-editor.dev/docs Generated from: src/content/docs/*.mdx (14 files) --- # DocxReviewer API Source: https://www.docx-editor.dev/docs/agent-api `@eigenpal/docx-editor-agents` — headless, Word-like API for AI document review. ```bash npm install @eigenpal/docx-editor-agents ``` ## Quick Start ```ts import { DocxReviewer } from '@eigenpal/docx-editor-agents'; const reviewer = await DocxReviewer.fromBuffer(buffer, 'AI Reviewer'); // Read — plain text with paragraph indices for LLM prompts const text = reviewer.getContentAsText(); // [0] (h1) Introduction // [1] The liability cap is $50k per incident. // [2] (table, row 1, col 1) Revenue ... // Comment on a paragraph (anchors to whole paragraph) reviewer.addComment(1, 'This cap seems too low.'); // Replace text (creates tracked change) reviewer.replace(1, '$50k', '$500k'); // Or batch from an LLM JSON response reviewer.applyReview({ comments: [{ paragraphIndex: 1, text: 'Too low.' }], proposals: [{ paragraphIndex: 1, search: '$50k', replaceWith: '$500k' }], }); // Export const output = await reviewer.toBuffer(); ``` ## DocxReviewer Methods Author is set once via `DocxReviewer.fromBuffer(buffer, 'Author Name')` — no need to repeat it on every call. ### Read | Method | Description | | --- | --- | | `DocxReviewer.fromBuffer(buffer: ArrayBuffer, author?: string \| undefined): Promise` | Create a reviewer from a DOCX file buffer. | | `getContent(options?: GetContentOptions \| undefined): ContentBlock[]` | Get document content as structured blocks (headings, paragraphs, tables, lists). | | `getContentAsText(options?: GetContentOptions \| undefined): string` | Get document content as plain text for LLM prompts. Each paragraph is prefixed with its index: `[0] text`, `[1] text`, etc. Table cells include position: `[5] (table, row 1, col 2) cell text`. Avoids JSON quote-escaping issues — LLMs can copy text verbatim. | ### Discover | Method | Description | | --- | --- | | `getChanges(filter?: ChangeFilter \| undefined): ReviewChange[]` | Get all tracked changes in the document. | | `getComments(filter?: CommentFilter \| undefined): ReviewComment[]` | Get all comments with their replies. | ### Comment | Method | Description | | --- | --- | | `addComment(paragraphIndex: number, text: string): number` | Add a comment on a paragraph. | | `addComment(options: AddCommentOptions): number` | _(overload)_ | | `replyTo(commentId: number, text: string): number` | Reply to an existing comment. | | `replyTo(commentId: number, options: ReplyOptions): number` | _(overload)_ | ### Propose Changes | Method | Description | | --- | --- | | `replace(paragraphIndex: number, search: string, replaceWith: string): void` | Replace text in a paragraph. Creates a tracked change (deletion + insertion). | | `replace(options: ProposeReplacementOptions): void` | _(overload)_ | | `proposeInsertion(options: ProposeInsertionOptions): void` | Insert text as a tracked change. | | `proposeDeletion(options: ProposeDeletionOptions): void` | Delete text as a tracked change. | ### Resolve | Method | Description | | --- | --- | | `acceptChange(id: number): void` | Accept a tracked change by its revision ID. | | `rejectChange(id: number): void` | Reject a tracked change by its revision ID. | | `acceptAll(): number` | Accept all tracked changes. Returns count accepted. | | `rejectAll(): number` | Reject all tracked changes. Returns count rejected. | ### Batch | Method | Description | | --- | --- | | `applyReview(ops: BatchReviewOptions): BatchResult` | Apply multiple review operations in one call. Uses the reviewer's default author. Individual failures are collected, not thrown. | ### Export | Method | Description | | --- | --- | | `toDocument(): Document` | Get the modified Document model. | | `toBuffer(): Promise` | Serialize back to a DOCX buffer. Requires the original buffer. | ## Types ### GetContentOptions | Field | Type | Description | | --- | --- | --- | | `fromIndex?` | `number` | | | `toIndex?` | `number` | | | `includeTrackedChanges?` | `boolean` | Annotate tracked changes inline. Default: true | | `includeCommentAnchors?` | `boolean` | Annotate comments inline. Default: true | ### BatchReviewOptions | Field | Type | Description | | --- | --- | --- | | `accept?` | `number[]` | | | `reject?` | `number[]` | | | `comments?` | `AddCommentOptions[]` | | | `replies?` | `(ReplyOptions & \{ commentId: number; \})[]` | | | `proposals?` | `ProposeReplacementOptions[]` | | ### BatchResult | Field | Type | Description | | --- | --- | --- | | `accepted` | `number` | | | `rejected` | `number` | | | `commentsAdded` | `number` | | | `repliesAdded` | `number` | | | `proposalsAdded` | `number` | | | `errors` | `BatchError[]` | | ### BatchError | Field | Type | Description | | --- | --- | --- | | `operation` | `string` | | | `id?` | `number` | | | `search?` | `string` | | | `error` | `string` | | ### ReviewChange | Field | Type | Description | | --- | --- | --- | | `id` | `number` | | | `type` | `'insertion' \| 'deletion' \| 'moveFrom' \| 'moveTo'` | | | `author` | `string` | | | `date` | `string \| null` | | | `text` | `string` | | | `context` | `string` | | | `paragraphIndex` | `number` | | ### ReviewComment | Field | Type | Description | | --- | --- | --- | | `id` | `number` | | | `author` | `string` | | | `date` | `string \| null` | | | `text` | `string` | | | `anchoredText` | `string` | | | `paragraphIndex` | `number` | | | `replies` | `ReviewCommentReply[]` | | | `done` | `boolean` | | ### AddCommentOptions | Field | Type | Description | | --- | --- | --- | | `paragraphIndex` | `number` | | | `text` | `string` | | | `author?` | `string` | | | `search?` | `string` | Optional: anchor to specific text. Omit to anchor whole paragraph. | ### ProposeReplacementOptions | Field | Type | Description | | --- | --- | --- | | `paragraphIndex` | `number` | | | `search` | `string` | | | `replaceWith` | `string` | | | `author?` | `string` | | ## Examples ### Next.js API Route — AI Document Review Upload a DOCX, send it to an LLM for review, apply comments and tracked changes, return the modified file. ```ts // app/api/review/route.ts import { NextRequest, NextResponse } from 'next/server'; import OpenAI from 'openai'; import { DocxReviewer } from '@eigenpal/docx-editor-agents'; const openai = new OpenAI(); export async function POST(request: NextRequest) { const formData = await request.formData(); const file = formData.get('file') as File; if (!file) return NextResponse.json({ error: 'No file' }, { status: 400 }); // 1. Parse DOCX and read as plain text const reviewer = await DocxReviewer.fromBuffer(await file.arrayBuffer(), 'AI Reviewer'); const text = reviewer.getContentAsText(); // 2. Send to LLM const response = await openai.chat.completions.create({ model: 'gpt-4o', response_format: { type: 'json_object' }, messages: [ { role: 'system', content: `Review this document. Return JSON: { "comments": [{ "paragraphIndex": , "text": "" }], "replacements": [{ "paragraphIndex": , "search": "", "replaceWith": "" }] } paragraphIndex must match a [number] from the document.` }, { role: 'user', content: text } ], }); const actions = JSON.parse(response.choices[0]?.message?.content || '{}'); // 3. Apply review — author is set once on the reviewer reviewer.applyReview({ comments: actions.comments, proposals: actions.replacements, }); // 4. Return modified DOCX const output = await reviewer.toBuffer(); return new NextResponse(output, { headers: { 'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', }, }); } ``` ### Node.js Script — Batch Review Review multiple DOCX files from the command line. ```ts import { readFileSync, writeFileSync } from 'fs'; import { DocxReviewer } from '@eigenpal/docx-editor-agents'; const buffer = readFileSync('contract.docx'); const reviewer = await DocxReviewer.fromBuffer(buffer.buffer, 'Legal Bot'); // Read content const content = reviewer.getContentAsText(); console.log(content); // [0] (h1) Service Agreement // [1] The liability cap is $50k per incident. // [2] (table, row 1, col 1) Party A ... // Add comments and changes directly reviewer.addComment(1, 'Liability cap is below industry standard.'); reviewer.replace(1, '$50k', '$500k'); // Export const output = await reviewer.toBuffer(); writeFileSync('contract-reviewed.docx', Buffer.from(output)); ``` ### Using with Claude (Anthropic) ```ts import Anthropic from '@anthropic-ai/sdk'; import { DocxReviewer } from '@eigenpal/docx-editor-agents'; const client = new Anthropic(); const reviewer = await DocxReviewer.fromBuffer(buffer, 'Claude Reviewer'); const response = await client.messages.create({ model: 'claude-sonnet-4-20250514', max_tokens: 4096, messages: [{ role: 'user', content: `Review this document and return JSON with comments and replacements:\n\n${reviewer.getContentAsText()}` }], }); const actions = JSON.parse(response.content[0].text); reviewer.applyReview({ comments: actions.comments, proposals: actions.replacements }); ``` Full working example: [examples/agent-use-demo](https://github.com/eigenpal/docx-editor/tree/main/examples/agent-use-demo) --- Source: [`@eigenpal/docx-editor-agents`](https://github.com/eigenpal/docx-editor/tree/main/packages/agent-use) --- # Interactive Examples Source: https://www.docx-editor.dev/docs/examples ## Playground Try the full interactive playground to toggle props and see changes in real time: ## Read-Only Mode Compare the full editor with a read-only preview. The `readOnly` prop strips the toolbar and all editing UI: ## Mode Toggle Switch between editing, suggesting, and viewing modes to see how the editor behaves in each: ## Toolbar Customization Customize the title bar with a logo, document name, and right-side actions: ## Author Attribution Set the `author` prop to attribute comments and track changes to specific users: --- # Getting Started Source: https://www.docx-editor.dev/docs/getting-started ## Overview `@eigenpal/docx-js-editor` is an open-source WYSIWYG document editor for React that lets users edit `.docx` files directly in the browser. It provides a Word-like editing experience with rich formatting, tables, images, track changes, comments, and more — all client-side with no server required. ## Quick Install ```bash npm install @eigenpal/docx-js-editor ``` Or with your preferred package manager: ```bash # Yarn yarn add @eigenpal/docx-js-editor # pnpm pnpm add @eigenpal/docx-js-editor # Bun bun add @eigenpal/docx-js-editor ``` ## Minimal Setup ```tsx import { DocxEditor } from '@eigenpal/docx-js-editor'; import '@eigenpal/docx-js-editor/styles.css'; function App() { const [buffer, setBuffer] = useState(null); useEffect(() => { fetch('/template.docx') .then((res) => res.arrayBuffer()) .then(setBuffer); }, []); if (!buffer) return
Loading...
; return ( ); } ``` That's it — you now have a fully functional document editor in your React app. ## Try It Live Toggle props and see the editor respond in real time: ## Key Features - **Rich text editing** — bold, italic, underline, strikethrough, colors, fonts, sizes - **Tables** — insert, resize, merge/split cells - **Images** — insert, resize, drag-and-drop - **Page layout** — margins, orientation, page breaks, headers/footers - **Track changes** — suggesting mode with accept/reject - **Comments** — add, reply, resolve - **Zoom & rulers** — adjustable zoom level and document rulers - **Customizable toolbar** — render props and compound component APIs - **Plugin system** — extend with EditorPlugins (browser) and CorePlugins (Node.js) - **Translations (i18n)** — localize the editor UI into any language, community contributions welcome - **Export** — save back to `.docx` format ## Next Steps - [Installation](/docs/installation) — framework-specific setup for Next.js, Vite, Remix, Astro, and Vue - [Props Reference](/docs/props) — full list of configuration props and ref methods - [Toolbar Customization](/docs/toolbar) — customize the toolbar with logos, actions, and extra buttons - [Editor Modes](/docs/modes) — editing, suggesting, viewing, and read-only modes - [Translations (i18n)](/docs/translations) — localize the editor UI into any language, [contribute a locale](/docs/translations#contributing-a-new-locale) - [Examples](/docs/examples) — interactive demos with live prop toggling --- # Installation Source: https://www.docx-editor.dev/docs/installation ## Prerequisites - **React 18+** (or React 19) - **Node.js 18+** ## Install the Package ```bash npm install @eigenpal/docx-js-editor ``` Don't forget to import the styles: ```tsx import '@eigenpal/docx-js-editor/styles.css'; ``` ## Next.js Use the editor in any `'use client'` component: ```tsx // components/Editor.tsx 'use client'; import { DocxEditor } from '@eigenpal/docx-js-editor'; import '@eigenpal/docx-js-editor/styles.css'; export function Editor({ buffer }: { buffer: ArrayBuffer }) { return ; } ``` ## Vite (React) Works out of the box — no special configuration needed: ```tsx import { useState, useEffect } from 'react'; import { DocxEditor } from '@eigenpal/docx-js-editor'; import '@eigenpal/docx-js-editor/styles.css'; function App() { const [buffer, setBuffer] = useState(null); useEffect(() => { fetch('/template.docx') .then((res) => res.arrayBuffer()) .then(setBuffer); }, []); if (!buffer) return
Loading...
; return ; } ``` ## Remix Use `ClientOnly` to prevent server rendering: ```tsx import { ClientOnly } from 'remix-utils/client-only'; import { DocxEditor } from '@eigenpal/docx-js-editor'; import '@eigenpal/docx-js-editor/styles.css'; export default function EditorRoute() { const [buffer, setBuffer] = useState(null); useEffect(() => { fetch('/template.docx') .then((res) => res.arrayBuffer()) .then(setBuffer); }, []); return ( Loading editor...}> {() => buffer && } ); } ``` ## Astro Use a React island with `client:only="react"`: ```astro --- // src/pages/editor.astro --- ``` ```tsx // src/components/EditorWrapper.tsx import { useState, useEffect } from 'react'; import { DocxEditor } from '@eigenpal/docx-js-editor'; import '@eigenpal/docx-js-editor/styles.css'; export default function EditorWrapper() { const [buffer, setBuffer] = useState(null); useEffect(() => { fetch('/template.docx') .then((res) => res.arrayBuffer()) .then(setBuffer); }, []); if (!buffer) return
Loading...
; return ; } ``` ## Vue (via Web Component wrapper) The editor is a React component, but you can wrap it for Vue using `@veaury/vue-react`: ```bash npm install @eigenpal/docx-js-editor @veaury/vue-react react react-dom ``` ```vue ``` ## Example Repositories Full working examples for each framework are available on GitHub: - [Next.js Example](https://github.com/eigenpal/docx-js-editor/tree/main/examples/nextjs) - [Vite Example](https://github.com/eigenpal/docx-js-editor/tree/main/examples/vite) - [Remix Example](https://github.com/eigenpal/docx-js-editor/tree/main/examples/remix) - [Astro Example](https://github.com/eigenpal/docx-js-editor/tree/main/examples/astro) --- # Editor Modes Source: https://www.docx-editor.dev/docs/modes ## Overview The editor supports four modes that control how users interact with the document: | Mode | Prop | Description | | --- | --- | --- | | **Editing** | `mode="editing"` | Full editing — changes are applied directly | | **Suggesting** | `mode="suggesting"` | Track changes — edits are recorded as suggestions | | **Viewing** | `mode="viewing"` | Read-only with toolbar visible — users can zoom, print, navigate | | **Read-Only** | `readOnly={true}` | Minimal preview — no toolbar, no rulers, no interaction UI | ## Editing Mode The default mode. Users can freely edit the document — type text, format, insert tables and images, etc. All changes are applied directly to the document. ```tsx ``` ## Suggesting Mode Enables track changes. All edits are recorded as insertions and deletions that can be accepted or rejected. This is useful for collaborative editing workflows. ```tsx ``` The `author` prop sets who is credited for each tracked change. The toolbar shows accept/reject controls for suggestions. ## Viewing Mode A read-only mode that keeps the toolbar visible. Users can zoom, print, navigate headings, and use the document outline — but they cannot edit content. This is the right choice for document review UIs where users still need toolbar controls. ```tsx ``` ## Read-Only Mode The most minimal mode. Hides the toolbar, rulers, and all editing UI. The document is rendered as a static preview. Use this for embedding documents in pages where no interaction is needed. ```tsx ``` ## Controlled Mode Switching You can let users switch modes at runtime using `onModeChange`: ```tsx function App() { const [mode, setMode] = useState<'editing' | 'suggesting' | 'viewing'>('editing'); return (
); } ``` The `onModeChange` callback fires when the user switches modes through the toolbar's built-in mode selector (if visible). Passing both `mode` and `onModeChange` makes it a controlled component. ## Mode Comparison | Feature | Editing | Suggesting | Viewing | Read-Only | | --- | --- | --- | --- | --- | | Text editing | Yes | Yes (tracked) | No | No | | Toolbar visible | Yes | Yes | Yes | No | | Rulers | Configurable | Configurable | Configurable | No | | Zoom controls | Yes | Yes | Yes | No | | Print | Yes | Yes | Yes | No | | Comments | Yes | Yes | View only | No | | Track changes UI | No | Yes | View only | No | | Document outline | Configurable | Configurable | Configurable | No | --- # CorePlugin & Headless API Source: https://www.docx-editor.dev/docs/plugins/core-plugins ## What is the Headless API? The package has two entry points: ``` src/index.ts → Browser editor (React, needs DOM) src/headless.ts → Headless API (Node.js, no DOM needed) ``` The headless API gives you `DocumentAgent`, parsers, serializers, and template processing — everything you need to manipulate DOCX files programmatically in Node.js. No browser, no React, no ProseMirror. ```ts import { DocumentAgent, parseDocx, processTemplate } from '@eigenpal/docx-js-editor/headless'; ``` **CorePlugins** extend the headless API with custom command handlers. They're the server-side equivalent of EditorPlugins. ## When to Use the Headless API - **API routes** — fill templates, generate documents server-side - **CI/CD pipelines** — validate templates, extract variables - **Node.js scripts** — batch-process DOCX files - **Server-side agents** — programmatic document manipulation If you need UI panels, overlays, or ProseMirror decorations, use an [EditorPlugin](/docs/plugins/editor-plugins) instead. ## DocumentAgent `DocumentAgent` is the main entry point for headless document manipulation: ```ts import { DocumentAgent } from '@eigenpal/docx-js-editor/headless'; import fs from 'fs'; // Load a DOCX file const buffer = fs.readFileSync('template.docx'); const agent = await DocumentAgent.fromBuffer(buffer); // Read content console.log('Word count:', agent.getWordCount()); console.log('Variables:', agent.getVariables()); // Edit const edited = agent .insertText({ paragraphIndex: 0, offset: 0 }, 'Hello ') .applyStyle(0, 'Heading1'); // Fill template variables const filled = await edited.applyVariables({ customer_name: 'Jane Doe', date: '2024-02-15', }); // Export const output = await filled.toBuffer(); fs.writeFileSync('output.docx', Buffer.from(output)); ``` ### In a Next.js API Route ```ts // app/api/fill-template/route.ts import { processTemplate } from '@eigenpal/docx-js-editor/headless'; export async function POST(req: Request) { const formData = await req.formData(); const file = formData.get('file') as File; const variables = JSON.parse(formData.get('variables') as string); const buffer = await file.arrayBuffer(); const filled = await processTemplate(buffer, variables); return new Response(filled, { headers: { 'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', }, }); } ``` ### Template Processing Utilities ```ts import { processTemplate, getTemplateTags, validateTemplate, } from '@eigenpal/docx-js-editor/headless'; // Get all template variables from a DOCX const tags = await getTemplateTags(buffer); // → ['name', 'date', 'items', 'total'] // Validate template syntax const result = await validateTemplate(buffer); // → { valid: true } or { valid: false, errors: [...] } // Fill template with data const filled = await processTemplate(buffer, { name: 'Jane Doe', date: '2024-02-15', items: [{ name: 'Widget', price: 9.99 }], total: '$9.99', }); ``` ## CorePlugin Interface CorePlugins extend `DocumentAgent` with custom command handlers: ```ts interface CorePlugin { id: string; name: string; version?: string; description?: string; commandHandlers?: Record; initialize?: () => void | Promise; destroy?: () => void | Promise; dependencies?: string[]; } ``` ### Fields | Field | Required | Description | | ----------------- | -------- | -------------------------------------------- | | `id` | Yes | Unique identifier | | `name` | Yes | Human-readable name | | `version` | No | Semver version string | | `description` | No | Short description | | `commandHandlers` | No | Map of command type → handler function | | `initialize` | No | Called once during registration | | `destroy` | No | Cleanup on unregistration | | `dependencies` | No | IDs of plugins that must be registered first | ## Command Handlers A command handler is a pure function that receives a `Document` and a command, then returns a new `Document`: ```ts type CommandHandler = (doc: Document, command: PluginCommand) => Document; interface PluginCommand { type: string; id?: string; position?: Position; range?: Range; [key: string]: unknown; } ``` Example — a plugin that adds watermark text: ```ts import type { CorePlugin, PluginCommand } from '@eigenpal/docx-js-editor'; import type { Document } from '@eigenpal/docx-js-editor'; const watermarkPlugin: CorePlugin = { id: 'watermark', name: 'Watermark', commandHandlers: { addWatermark(doc: Document, cmd: PluginCommand) { const text = (cmd as { text: string }).text; // ... transform doc to add watermark header return doc; }, }, }; ``` Use it: ```ts import { pluginRegistry } from '@eigenpal/docx-js-editor'; pluginRegistry.register(watermarkPlugin); const handler = pluginRegistry.getCommandHandler('addWatermark'); if (handler) { const newDoc = handler(doc, { type: 'addWatermark', text: 'DRAFT' }); } ``` ## PluginRegistry The global `pluginRegistry` manages all CorePlugins: ```ts import { pluginRegistry } from '@eigenpal/docx-js-editor'; // Register pluginRegistry.register(myPlugin); // Query pluginRegistry.has('watermark'); // true pluginRegistry.getAll(); // CorePlugin[] pluginRegistry.getCommandTypes(); // ['addWatermark'] // Unregister pluginRegistry.unregister('watermark'); // Batch registration import { registerPlugins } from '@eigenpal/docx-js-editor'; registerPlugins([pluginA, pluginB]); ``` ## Reference Implementation: Docxtemplater Plugin The built-in `docxtemplaterPlugin` in `src/core-plugins/docxtemplater/` is a full reference: - **Command handlers**: `insertTemplateVariable`, `replaceWithTemplateVariable` - Lazy dependency validation — `processTemplate` checks for `docxtemplater`/`pizzip` at call time ```ts import { pluginRegistry, docxtemplaterPlugin } from '@eigenpal/docx-js-editor'; pluginRegistry.register(docxtemplaterPlugin); // Now DocumentAgent can dispatch insertTemplateVariable commands ``` Note: there is also a separate **EditorPlugin** for template UI (`src/plugins/template/`) that handles syntax highlighting and the annotation panel in the browser. The two systems are independent but complement each other — a single feature can span both. ## Next Steps - [EditorPlugin API](/docs/plugins/editor-plugins) — browser-side UI plugins - [Examples & Cookbook](/docs/plugins/examples) — advanced patterns - [Getting Started](/docs/plugins/getting-started) — overview and hello world --- # EditorPlugin API Source: https://www.docx-editor.dev/docs/plugins/editor-plugins EditorPlugins run in the browser alongside `DocxEditor`. They can contribute UI panels, document overlays, ProseMirror plugins, and scoped CSS. ## How It Works `PluginHost` wraps `DocxEditor` and manages the plugin lifecycle: Internally, `PluginHost` uses `React.cloneElement` to inject props into the child `DocxEditor`. This means `DocxEditor` **must** be the direct child of `PluginHost`. ## Quick Start ```tsx import { DocxEditor, PluginHost, templatePlugin } from '@eigenpal/docx-js-editor'; function Editor({ file }: { file: ArrayBuffer }) { return ( ); } ``` ## EditorPlugin\ ```ts interface EditorPlugin { id: string; name: string; proseMirrorPlugins?: ProseMirrorPlugin[]; Panel?: React.ComponentType>; panelConfig?: PanelConfig; onStateChange?: (view: EditorView) => TState | undefined; initialize?: (view: EditorView | null) => TState; destroy?: () => void; styles?: string; renderOverlay?: ( context: RenderedDomContext, state: TState, editorView: EditorView | null ) => ReactNode; } ``` ### Fields | Field | Required | Description | | -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- | | `id` | Yes | Unique identifier. Used as key for state storage and CSS `