Live demo
Upload a .docx or edit the sample below. Nothing leaves your browser.
Install
npm install @eigenpal/docx-js-editorEditor component
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>
);
}No "use client" directive needed. Vite has no server/client module boundary.
Use it in your app
import { MyDocxEditor } from "./components/DocxEditor";
function App() {
return <MyDocxEditor />;
}
export default App;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. No reload.
Save to a backend
const saved = await editorRef.current?.save();
await fetch("https://your-api.com/documents", {
method: "POST",
body: saved,
});save() returns a raw ArrayBuffer.
Common errors
| Error | Fix |
|---|---|
| Styles not rendering | Import @eigenpal/docx-js-editor/styles.css in your component |
| Blank editor | Ensure the ArrayBuffer is loaded before rendering <DocxEditor> |
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
- Vite example on GitHub
- Track changes and comments for review workflows
- Document templates with variable placeholders
- Full docs for all props and config