To add DOCX editing to a Vue 3 app, install @docx-editor.dev/vue, pass the document bytes, and render <DocxEditor>. The editor parses and paints the document in the browser. Your app uploads the document only if you add upload code.
This guide covers installation, loading, saving, review features, automation, localization, and licensing.
Live demo
Install
Install the adapter and its engine peer:
npm install @docx-editor.dev/vue @docx-editor.dev/core@docx-editor.dev/core and Vue 3 are peer dependencies. The adapter bundles the string catalog, so @docx-editor.dev/i18n is not a separate install.
Render the editor
Import the component and the stylesheet, then pass a document. The following example mounts an empty document:
<script setup lang="ts">
import { DocxEditor } from '@docx-editor.dev/vue';
import '@docx-editor.dev/vue/styles.css';
</script>
<template>
<div class="editor-host">
<DocxEditor document="blank" />
</div>
</template>
<style>
.editor-host {
height: 100vh;
}
</style>The document prop accepts an ArrayBuffer, a Uint8Array, a DocumentHandle, or the string 'blank'. If you omit the prop, the editor waits for bytes and keeps its controls disabled.
<DocxEditor> renders the title bar, menu, toolbar, navigation pane, hyperlink popover, context menu, and editable document. Set a nonzero height on its parent because the editor fills that parent.
Load a .docx file
To load a document from a URL, fetch the bytes and assign them to a ref:
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { DocxEditor } from '@docx-editor.dev/vue';
const props = defineProps<{ url: string }>();
const bytes = ref<Uint8Array>();
onMounted(async () => {
const response = await fetch(props.url);
bytes.value = new Uint8Array(await response.arrayBuffer());
});
</script>
<template>
<DocxEditor :document="bytes" />
</template>To load a document from a file picker, read the selected file as an ArrayBuffer:
<script setup lang="ts">
import { ref } from 'vue';
import { DocxEditor } from '@docx-editor.dev/vue';
const bytes = ref<Uint8Array>();
async function onFile(event: Event) {
const file = (event.target as HTMLInputElement).files?.[0];
if (file) bytes.value = new Uint8Array(await file.arrayBuffer());
}
</script>
<template>
<input type="file" accept=".docx" @change="onFile" />
<DocxEditor v-if="bytes" :document="bytes" />
</template>The editor parses OOXML in the browser. It sends no file data to a server.
Save a .docx file
Call save() on the component ref. It returns Promise<ArrayBuffer | null>, and it returns null when no document is open.
The following example downloads the edited document:
<script setup lang="ts">
import { ref } from 'vue';
import { DocxEditor, type DocxEditorRef } from '@docx-editor.dev/vue';
const DOCX_MIME =
'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
const editorRef = ref<DocxEditorRef | null>(null);
async function download() {
const buffer = await editorRef.value?.save();
if (!buffer) return;
const blob = new Blob([buffer], { type: DOCX_MIME });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'edited.docx';
link.click();
URL.revokeObjectURL(url);
}
</script>
<template>
<button @click="download">Download .docx</button>
<DocxEditor ref="editorRef" document="blank" />
</template>To send the document to your own server instead, pass the buffer to fetch:
<script setup lang="ts">
import { ref } from 'vue';
import { DocxEditor, type DocxEditorRef } from '@docx-editor.dev/vue';
defineProps<{ bytes: Uint8Array }>();
const editorRef = ref<DocxEditorRef | null>(null);
async function save() {
const buffer = await editorRef.value?.save();
if (!buffer) return;
await fetch('/api/documents/42', { method: 'PUT', body: buffer });
}
</script>
<template>
<button @click="save">Save</button>
<DocxEditor ref="editorRef" :document="bytes" />
</template>save() returns the same OOXML format that the editor reads. It serializes modeled XML, retains unmodeled XML structurally, and preserves binary package payloads byte for byte.
Use the editor in Nuxt
The editor needs browser layout APIs, so Nuxt must mount it from a client-only
component. The Nuxt DOCX editor guide covers the
.client.vue component, stylesheet setup, Vite dependency optimization, file
loading, and saving. The Nuxt setup reference
provides the shorter configuration.
Add tracked changes and comments
Tracked changes and comments come from @docx-editor.dev/pro, which is a separate package. It is commercially licensed: free to evaluate, and production use needs an agreement.
Install it beside the adapter:
npm install @docx-editor.dev/proRegister reviewModule through the modules prop and add the review rail inside the viewport. Compose the editor from its parts, because the review rail is a child of the viewport:
<script setup lang="ts">
import {
DocxEditorContent,
DocxEditorRoot,
DocxEditorToolbar,
DocxEditorViewport,
} from '@docx-editor.dev/vue';
import { DocxEditorReview, reviewModule } from '@docx-editor.dev/pro/vue';
defineProps<{ bytes: Uint8Array }>();
const modules = [reviewModule()];
</script>
<template>
<DocxEditorRoot :document="bytes" :modules="modules" author="Jess Lin">
<DocxEditorToolbar />
<DocxEditorViewport>
<DocxEditorContent />
<DocxEditorReview />
</DocxEditorViewport>
</DocxEditorRoot>
</template>The author prop names the identity written into a revision or a comment reply.
To make every edit a tracked change, set mode to suggesting:
<script setup lang="ts">
import { ref } from 'vue';
import { DocxEditor, type EditorMode } from '@docx-editor.dev/vue';
defineProps<{ bytes: Uint8Array; reviewer: string }>();
const mode = ref<EditorMode>('suggesting');
</script>
<template>
<DocxEditor :document="bytes" :author="reviewer" :mode="mode" />
</template>mode accepts 'edit', 'view', or 'suggesting'. In suggesting mode, the editor wraps each edit in revision markup and attributes it to author. A reviewer accepts or rejects those revisions later.
For more information, see Tracked changes and Comments.
Automate an open document
@docx-editor.dev/editor-api provides programmatic document operations. Its /browser entry connects to an editor that the page already created.
createBrowser needs the editor instance, which useDocxEditor() returns. That composable reads the instance from the editor's own provide/inject tree, so call it from a component rendered inside <DocxEditorRoot>:
<!-- BoldFirstParagraph.vue -->
<script setup lang="ts">
import { useDocxEditor } from '@docx-editor.dev/vue';
import { DocxEditor } from '@docx-editor.dev/editor-api/browser';
const editor = useDocxEditor();
async function emboldenFirstParagraph() {
if (!editor.value) return;
const runtime = DocxEditor.createBrowser(editor.value, {
author: 'Demo Reviewer',
});
await runtime.run(async (context) => {
const heading = context.document.body.paragraphs.getFirstOrNullObject();
heading.load('text');
await context.sync();
if (!heading.isNullObject) heading.font.bold = true;
await context.sync();
});
}
</script>
<template>
<button @click="emboldenFirstParagraph">Bold the first paragraph</button>
</template>Mount that component as a child of the editor root:
<script setup lang="ts">
import {
DocxEditorContent,
DocxEditorRoot,
DocxEditorToolbar,
DocxEditorViewport,
} from '@docx-editor.dev/vue';
import BoldFirstParagraph from './BoldFirstParagraph.vue';
defineProps<{ bytes: Uint8Array }>();
</script>
<template>
<DocxEditorRoot :document="bytes">
<DocxEditorToolbar />
<BoldFirstParagraph />
<DocxEditorViewport>
<DocxEditorContent />
</DocxEditorViewport>
</DocxEditorRoot>
</template>The getEditor() method on the component ref is not a substitute. It returns the narrower Editor facade, and createBrowser does not accept it.
The object model follows the Office.js Word API, so code written for a Word add-in runs against it. The package ships no model integration, tool catalog, or chat UI. Your application decides which operations to expose to a model. For more information, see the editing API.
Localize the interface
The adapter bundles @docx-editor.dev/i18n. It includes English, German, French, Hebrew, Hindi, Indonesian, Polish, Brazilian Portuguese, Turkish, and Simplified Chinese.
Pass a catalog through the i18n prop:
<script setup lang="ts">
import { DocxEditor } from '@docx-editor.dev/vue';
import { pl } from '@docx-editor.dev/i18n';
defineProps<{ bytes: Uint8Array }>();
</script>
<template>
<DocxEditor :document="bytes" :i18n="pl" locale="pl" />
</template>Every catalog except English is partial. The editor falls back to the English string for a key a translation omits. For more information, see Translations.
License
@docx-editor.dev/vue is open source under Apache 2.0. You can use it in personal and commercial projects, modify the source, and redistribute it. There are no usage limits and no watermarks.
@docx-editor.dev/pro and @docx-editor.dev/editor-api are commercially licensed. Both are free to evaluate, and production use needs an agreement.
The packages/vue/ directory contains the source in eigenpal/docx-editor on GitHub.
Compare with other Vue Word editors
Vue applications can use several types of .docx editor:
- Editors that convert OOXML to HTML. Conversion can omit tables of contents, page layout, and revision markup.
- Software as a service (SaaS) components that process documents on the provider's server.
- React libraries with a Vue wrapper. These libraries include React in the application bundle and hydration process.
@docx-editor.dev/vue is a Vue 3 adapter over a shared engine. It uses the same OOXML parser and the same command set as the React adapter, without a React wrapper. Editing happens in the browser.
Where to go next
- Vue DOCX viewer covers document preview, Word fidelity, and industry applications.
- Vue and React DOCX editor APIs compared explains the adapter differences.
- Vue package overview describes the full surface.
- Vue props reference documents every prop.
- Vue examples collects copy-paste patterns.
- Vue API reference is generated from the type declarations.
- Vite with Vue setup covers a Vite project.