Live demo
This editor runs in the Next.js app you're reading right now. Upload a .docx or edit the sample below. Nothing leaves your browser.
Install
npm install @eigenpal/docx-js-editorEditor component
"use client";
import { useState, useEffect, useRef, useCallback } from "react";
import { DocxEditor } from "@eigenpal/docx-js-editor";
import type { DocxEditorRef } from "@eigenpal/docx-js-editor";
import "@eigenpal/docx-js-editor/styles.css";
export function MyDocxEditor() {
const editorRef = useRef<DocxEditorRef>(null);
const [buffer, setBuffer] = useState<ArrayBuffer | null>(null);
useEffect(() => {
fetch("/sample.docx")
.then((res) => res.arrayBuffer())
.then(setBuffer);
}, []);
const handleSave = useCallback(async () => {
const saved = await editorRef.current?.save();
if (!saved) return;
const blob = new Blob([saved], {
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
});
const url = URL.createObjectURL(blob);
Object.assign(document.createElement("a"), {
href: url,
download: "edited.docx",
}).click();
URL.revokeObjectURL(url);
}, []);
if (!buffer) return <div>Loading...</div>;
return (
<div style={{ height: "80vh" }}>
<DocxEditor
ref={editorRef}
documentBuffer={buffer}
showToolbar
showRuler
showZoomControl
/>
<button onClick={handleSave}>Download .docx</button>
</div>
);
}"use client" is required. The component uses refs, hooks, and browser globals.
Load it in a page
import { MyDocxEditor } from "@/components/DocxEditor";
export default function EditorPage() {
return <MyDocxEditor />;
}File uploads
function handleUpload(e: React.ChangeEvent<HTMLInputElement>) {
const file = e.target.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = () => setBuffer(reader.result as ArrayBuffer);
reader.readAsArrayBuffer(file);
}Swap the buffer and the editor re-renders with the new document. No reload.
Save to API route
// app/api/documents/route.ts
export async function POST(req: NextRequest) {
const data = await req.arrayBuffer();
// upload to S3, save to DB, etc.
return NextResponse.json({ ok: true });
}const saved = await editorRef.current?.save();
await fetch("/api/documents", { method: "POST", body: saved });Common errors
| Error | Fix |
|---|---|
| Styles not rendering | Import @eigenpal/docx-js-editor/styles.css in the client component |
What you get
The editor parses OOXML on the client and renders via ProseMirror. Out of the box: bold/italic/underline, tables with cell merging, inline images, headers and footers, page breaks, tracked changes, threaded comments, zoom, and document outline. It exports back to valid .docx. MIT licensed, ~200KB gzipped, no server dependency.
Next steps
- Next.js example on GitHub
- Track changes and comments for review workflows
- Document templates with variable placeholders
- Full docs for all props and config