This is a step-by-step guide to adding a Word document editor to a Vue 3 app. By the end you'll have <DocxEditor> mounted, loading a real .docx file, and saving it back, all in the browser.
@eigenpal/docx-editor-vue is the Vue 3 adapter for docx-editor. It's the same editor that ships as @eigenpal/docx-editor-react, with Vue idioms at the surface (composables, slots, events, kebab-case attributes) and the same OOXML parser underneath. The round-trip is lossless: open a Word file, edit it in your Vue app, reopen in Word with tracked changes, comments, tables, headers, footers, and page layout intact.
Step 1: install
npm install @eigenpal/docx-editor-vueThat one install pulls @eigenpal/docx-editor-core and @eigenpal/docx-editor-i18n transitively. Add @eigenpal/docx-editor-agents later if you want the AI agent toolkit.
Step 2: render the editor
<script setup lang="ts">
import { ref } from "vue";
import { DocxEditor } from "@eigenpal/docx-editor-vue";
import "@eigenpal/docx-editor-vue/styles.css";
const buf = ref<ArrayBuffer | null>(null);
</script>
<template>
<DocxEditor :document-buffer="buf" />
</template>A null buffer mounts an empty document. The component is client-only (ProseMirror reads DOM measurements at mount). On Nuxt 3 and 4, the @eigenpal/nuxt-docx-editor module registers it SSR-safe for you, so there's no <ClientOnly> wrapper to write.
Here is <DocxEditor> running. Edit the text, use the toolbar, or open your own .docx:
Step 3: load a .docx file
document-buffer takes an ArrayBuffer. Fetch a file and assign it. From a URL:
<script setup lang="ts">
import { ref, watchEffect } from "vue";
import { DocxEditor } from "@eigenpal/docx-editor-vue";
import "@eigenpal/docx-editor-vue/styles.css";
const buf = ref<ArrayBuffer | null>(null);
watchEffect(async () => {
buf.value = await fetch("/contract.docx").then((r) => r.arrayBuffer());
});
</script>
<template>
<DocxEditor :document-buffer="buf" />
</template>Or from a file picker, so users open their own documents:
<script setup lang="ts">
import { ref } from "vue";
import { DocxEditor } from "@eigenpal/docx-editor-vue";
const buf = ref<ArrayBuffer | null>(null);
async function onFile(e: Event) {
const file = (e.target as HTMLInputElement).files?.[0];
if (file) buf.value = await file.arrayBuffer();
}
</script>
<template>
<input type="file" accept=".docx" @change="onFile" />
<DocxEditor v-if="buf" :document-buffer="buf" />
</template>The editor parses OOXML in the browser. No upload, no server round-trip.
Step 4: save the document
<DocxEditor> exposes an imperative handle. Grab it with useTemplateRef, call save(), and you get an ArrayBuffer of OOXML bytes back:
<script setup lang="ts">
import { useTemplateRef } from "vue";
import { DocxEditor, type DocxEditorRef } from "@eigenpal/docx-editor-vue";
const editor = useTemplateRef<DocxEditorRef>("editor");
async function save() {
const buf = await editor.value?.save();
if (buf) await fetch("/api/documents/1", { method: "PUT", body: buf });
}
</script>
<template>
<button @click="save">Save</button>
<DocxEditor ref="editor" :document-buffer="buf" />
</template>The same handle also exposes addComment, proposeChange, findInDocument, scrollToParaId, scrollToPosition, and getDocument, with the same signatures as the React adapter's DocxEditorRef.
That's the whole loop: install, render, load, save. The rest of this post covers the Vue-specific surface (composables, events, slots) and the optional extras (i18n, AI agents).
Composables map one-to-one to React hooks
@eigenpal/docx-editor-vue/composables ships the same set of helpers as @eigenpal/docx-editor-react/hooks, idiomatic to Vue refs.
React (/hooks) | Vue (/composables) |
|---|---|
useHistory | useHistory |
useFindReplace | useFindReplace |
useAutoSave | useAutoSave |
useClipboard | useClipboard |
useTableSelection | useTableSelection |
useDragAutoScroll | useDragAutoScroll |
useSelectionHighlight | useSelectionHighlight |
useVisualLineNavigation | useVisualLineNavigation |
useFixedDropdown | useFixedDropdown |
useWheelZoom | useZoom (renamed for Vue convention) |
| (no equivalent in React) | useDocxEditor, useTrackedChanges, useCommentSidebarItems, useTableResize |
The Vue exclusives are documented as such in etc/parity.contract.json upstream. The reverse list (React-exclusive composables not yet in Vue) is empty as of 1.0.1.
Parity is checked in CI
A CI step walks the .d.ts of both adapters and diffs their public exports against etc/parity.contract.json. If a symbol ships in one adapter without a counterpart in the other (or a contract entry saying why not), the build fails. Symbols are either paired, marked deferred with a tracking issue, or marked exclusive with a reason. Adding a React export without a Vue sibling doesn't compile in CI.
Events instead of callbacks
React's onFoo callback props become Vue @foo events with the same payload shape.
| React prop | Vue event |
|---|---|
onChange(doc) | @change |
onSave(buf) | @save |
onModeChange(mode) | @mode-change |
onSelectionChange(state) | @selection-change |
onCommentAdd(comment) | @comment-add |
onCommentResolve(comment) | @comment-resolve |
onCommentDelete(comment) | @comment-delete |
onCommentReply(reply, parent) | @comment-reply |
onError(err) | @error |
onFontsLoaded() | @fonts-loaded |
Slots instead of render props
React's renderLogo and renderTitleBarRight callback props become Vue named slots:
<DocxEditor :document-buffer="buf" document-name="Q3 Memo">
<template #titleBarRight>
<SaveIndicator :dirty="dirty" />
</template>
</DocxEditor>i18n with per-locale chunking
<script setup>
import pl from "@eigenpal/docx-editor-i18n/pl";
import { DocxEditor } from "@eigenpal/docx-editor-vue";
</script>
<template>
<DocxEditor :document-buffer="buf" :i18n="pl" />
</template>Each locale code-splits via the per-locale subpath import. Restored in 1.0.1 after the 1.0 line shipped named-exports-only (which bundled the full 7-locale set into one chunk). Pin to ≥ 1.0.1.
The editor below runs with the Polish locale. Toolbar labels, tooltips, and dialogs are translated:
Agents
import { useDocxAgentTools } from "@eigenpal/docx-editor-agents/vue";Same 14 tools as the React composable, same OpenAI function-calling schemas, same transport. Server-side wiring through @eigenpal/docx-editor-agents/ai-sdk/server is framework-agnostic. Full client + server setup on Agents → Live editor.
Ask the agent below to review the document. Its comments and tracked changes land in the editor as the model streams tokens:
Where to next
- Vue package overview
- Vue props reference (all DocxEditorProps with examples)
- Vue examples (interactive demos)
- Vue API reference (auto-generated from the d.ts)
- 1.0 release post for the full restructure