Quickstart
Load, edit, and save a .docx in the browser with React or Vue. Minimal setup: a file input, the editor component, and a download button, in one file.
This page builds a browser editor that opens a local .docx and downloads the edited document.
Install
The adapter includes the string catalog and declares the engine as a peer dependency, so install both.
npm install @docx-editor.dev/react @docx-editor.dev/corenpm install @docx-editor.dev/vue @docx-editor.dev/coreOn Next.js, Nuxt, Remix, or other SSR frameworks the editor must render client-side; mounting it during SSR throws window is not defined. Use the recipe in Installation. The code below works without further configuration in client-rendered apps such as Vite.
Load, edit, save
This example puts load, edit, and save in one file. A file input supplies bytes to the editor, the editor supports editing (typing, formatting, undo, tables), and a button serializes the current state back to a .docx download.
The packaged <DocxEditor> shows a document page with loading status while a document opens.
// App.tsx
import { useRef, useState } from 'react';
import { DocxEditor, type DocxEditorRef } from '@docx-editor.dev/react';
import '@docx-editor.dev/core/styles/editor.css';
export default function App() {
const editorRef = useRef<DocxEditorRef>(null);
const [file, setFile] = useState<File | null>(null);
const [bytes, setBytes] = useState<Uint8Array>();
async function pick(e: React.ChangeEvent<HTMLInputElement>) {
const picked = e.target.files?.[0] ?? null;
setFile(picked);
setBytes(picked ? new Uint8Array(await picked.arrayBuffer()) : undefined);
}
async function download() {
const buffer = await editorRef.current?.save();
if (!buffer) return;
const blob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = file?.name ?? 'document.docx';
a.click();
URL.revokeObjectURL(url);
}
return (
<div style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
<div style={{ padding: 8, display: 'flex', gap: 8 }}>
<input type="file" accept=".docx" onChange={pick} />
<button onClick={download}>Download .docx</button>
</div>
<div style={{ flex: 1, minHeight: 0 }}>
{bytes && <DocxEditor ref={editorRef} document={bytes} mode="edit" />}
</div>
</div>
);
}<!-- App.vue -->
<script setup lang="ts">
import { ref, shallowRef } from 'vue';
import { DocxEditor, type DocxEditorRef } from '@docx-editor.dev/vue';
import '@docx-editor.dev/vue/styles.css';
const editorRef = ref<DocxEditorRef | null>(null);
const fileName = ref<string>();
const bytes = shallowRef<Uint8Array>();
async function pick(event: Event) {
const picked = (event.target as HTMLInputElement).files?.[0];
fileName.value = picked?.name;
bytes.value = picked ? new Uint8Array(await picked.arrayBuffer()) : undefined;
}
async function download() {
const buffer = await editorRef.value?.save();
if (!buffer) return;
const blob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName.value ?? 'document.docx';
a.click();
URL.revokeObjectURL(url);
}
</script>
<template>
<div class="app">
<div class="bar">
<input type="file" accept=".docx" @change="pick" />
<button type="button" @click="download">Download .docx</button>
</div>
<div class="surface">
<DocxEditor v-if="bytes" ref="editorRef" :document="bytes" mode="edit" />
</div>
</div>
</template>
<style>
.app {
height: 100vh;
display: flex;
flex-direction: column;
}
.bar {
padding: 8px;
display: flex;
gap: 8px;
}
.surface {
flex: 1;
min-height: 0;
}
</style>Notes for this example:
documenttakes aUint8Array, anArrayBuffer, aDocumentHandle, or'blank'for an empty document. Omitting it means no document at all. The editor shows its loading screen, and every control stays disabled until you supply bytes.modeis'edit'(default),'view', or'suggesting', and is read at mount. Remount to change it.save()on the ref returnsPromise<ArrayBuffer | null>: a complete.docx, ornullwhen there is no document. Parsing and serialization both happen in the browser; the document stays in the browser; no server upload occurs.- The editor fills its parent, so give the parent a non-zero CSS height. The stylesheet import is required once per app.
Fetch instead of a file input (optional)
Load a template from your own server instead:
const bytes = new Uint8Array(await fetch('/template.docx').then((r) => r.arrayBuffer()));
// <DocxEditor document={bytes} />import { useDocxSource } from '@docx-editor.dev/vue';
const { document: bytes, isLoading } = useDocxSource('/template.docx');
// <DocxEditor :document="bytes" />useDocxSource() fetches the resource and exposes loading state.
To react to the built-in Save action (Cmd+S, or File → Save) instead of adding your own button, handle the save event. It replaces the packaged behavior, so read the bytes from the ref:
<DocxEditor
ref={editorRef}
document={bytes}
onSave={async () => {
const buffer = await editorRef.current?.save();
if (buffer) await upload(buffer);
}}
/><script setup lang="ts">
async function onSave() {
const buffer = await editorRef.value?.save();
if (buffer) await upload(buffer);
}
</script>
<template>
<DocxEditor ref="editorRef" :document="bytes" @save="onSave" />
</template>Open a local .docx file
Run your dev server, click the file input, and pick a .docx, for example a document that includes tables and headers. The editor parses it client-side and the download button writes the current state back. Check the result in Microsoft Word. If something renders or saves incorrectly, file an issue with the document.
If you do not have a local file, try the live demo first.
Next steps
- Installation for Next.js, Nuxt, Remix, and Astro specifics
- React composition or Vue composition to replace the packaged chrome with your own
- Word fidelity if you evaluate feature support and round-trip behavior
- React props and Vue props for the full packaged-editor prop surface