@zzk-1015/vue-onlyoffice-local
| Install | |
|---|---|
npm install @zzk-1015/vue-onlyoffice-local |
|
| Latest Version: | 2.0.1 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 12, 2026 |
| Links: | GitHub · npm |
Vue OnlyOffice Local
A Vue 3 plugin for fully local OnlyOffice document editing, aligned with onlyoffice-web-local v2.0.0 (oo-offline SDK).
This plugin lets you embed a fully functional OnlyOffice editor in your Vue 3 application without requiring a backend Document Server. It bundles the upstream oo-offline SDK: documents open as in-memory blob URLs, the editor loads <baseUrl>/vendor/web-apps/apps/api/documents/api.js from your self-hosted OnlyOffice 9.x offline vendor build, and saving uses an imperative editor.save(format?) that resolves with the file buffer via a postMessage file-stream hook.
Features
- 🔒 Privacy-First: No data leaves the browser; the offline vendor build stays on your host.
- 📝 Full Vue 3 Support: Written in TypeScript with Composition API.
- ⚡ Reactive: Rebuilds the editor when
file,mode,baseUrl, orconfigchange. - 🌐 URL & Local File Support: Loads documents from
File/Blob/ http(s) URL. Remote URLs are converted to blob URLs when CORS allows, and pass through todocument.urlwhen not. - 🎛 Imperative
save(): Triggereditor.save('pdf' | undefined)via template ref and receive the saved buffer. - 🪪 Customizable: Extensive configuration via props / slots /
configHookmiddleware. - 📦 Type Safe: Complete TypeScript definitions;
vue-tscclean.
Installation
npm install @zzk-1015/vue-onlyoffice-local
Prerequisites
This plugin requires a self-hosted copy of the OnlyOffice 9.x offline vendor build. Concretely, you must serve these assets from the same origin as your Vue app:
<baseUrl>/vendor/web-apps/apps/api/documents/api.js(DocsAPI entry point)<baseUrl>/vendor/sdkjs/(editor core)<baseUrl>/vendor/web-apps/(editor UI applications)
The assets come from the upstream onlyoffice-web-local release (html/vendor directory inside the GitHub release zip). They are roughly 700 MB and are intentionally not bundled in this npm package - host them under <your app>/public/vendor/ (or any same-origin path you point baseUrl at).
❗ x2t WASM is no longer required at the application layer: conversion runs inside the offline editor build (
Offline.js+ browser-resident x2t). The legacysdkUrl/x2tUrlprops are kept for backward compatibility but are ignored by v2.0.0.
Grab the vendor build from the upstream release: https://github.com/sweetwisdom/onlyoffice-web-local/releases.
Usage
Global Registration
import { createApp } from 'vue';
import VueOnlyOfficeLocal from '@zzk-1015/vue-onlyoffice-local';
import '@zzk-1015/vue-onlyoffice-local/style.css';
const app = createApp(App);
app.use(VueOnlyOfficeLocal, {
// SDK loads <defaultBaseUrl>/vendor/web-apps/apps/api/documents/api.js
defaultBaseUrl: '/',
globalConfig: {
editorConfig: { lang: 'en' },
},
});
app.mount('#app');
Local Registration (Composition API)
1. Loading a Local File (from <input type="file">)
<template>
<div>
<input type="file" @change="handleFileChange" accept=".docx,.xlsx,.pptx" />
<div v-if="file" style="height: 600px;">
<VueOnlyOfficeLocal
:file="file"
:file-name="fileName"
:base-url="'/'"
@ready="onEditorReady"
@error="onEditorError"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { VueOnlyOfficeLocal } from '@zzk-1015/vue-onlyoffice-local';
const file = ref<File | null>(null);
const fileName = ref('');
const handleFileChange = (e: Event) => {
const target = e.target as HTMLInputElement;
if (target.files && target.files.length > 0) {
file.value = target.files[0];
fileName.value = target.files[0].name;
}
};
const onEditorReady = (editor: { save: (fmt?: string) => Promise<unknown> }) => {
console.log('Editor initialized:', editor);
};
const onEditorError = (err: Error) => {
console.error('Editor failed:', err);
};
</script>
2. Loading from a URL
<template>
<div style="height: 600px;">
<VueOnlyOfficeLocal
file="https://example.com/documents/report.docx"
file-name="report.docx"
:base-url="'/'"
@ready="onEditorReady"
/>
</div>
</template>
Note: When loading from a URL, the resource must allow Cross-Origin Resource Sharing (CORS). The plugin will try to
fetchit and turn the bytes into a blob URL; on CORS failure, it falls back to passing the URL through todocument.urlso the editor can load it directly.
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
baseUrl |
string |
/ |
Same-origin URL prefix where the OnlyOffice 9.x offline vendor build is hosted. The SDK loads <baseUrl>/vendor/web-apps/apps/api/documents/api.js. |
file |
`string | Blob | File` |
fileName |
string |
undefined |
Title shown in the editor (e.g., "my-doc.docx"). Inferred from File / URL when omitted. |
fileType |
string |
inferred | Extension (e.g., "docx", "xlsx", "pdf"). Inferred from fileName when omitted. |
mode |
`'edit' | 'view'` | 'edit' |
width |
string |
'100%' |
CSS width of the editor container. |
height |
string |
'100%' |
CSS height of the editor container. |
config |
OnlyOfficeConfig |
{} |
Deep merge override for the internal OnlyOffice config object. |
configHook |
`(config) => config | Promise | undefined |
sdkUrl |
string |
'libs/sdk.js' |
Deprecated in v2.0.0. Use baseUrl + a vendor build. |
x2tUrl |
string |
'libs/x2t.js' |
Deprecated in v2.0.0. x2t WASM no longer needed. |
theme |
`'light' | 'dark'` | 'light' |
Events
| Event | Payload | Description |
|---|---|---|
ready |
(editor: OfficeEditor) |
Editor instance is mounted; the save() handle is now usable. |
document-ready |
void |
Document finished loading inside the editor. |
state-change |
(modified: boolean) |
Document dirty state changed. |
request-close |
void |
The user attempted to close the editor. |
meta-change |
(title: string) |
Document title changed. |
save |
(buffer, fileName, fileType) |
Editor finished saving via downloadAs. (Available when used inside configHook or via the menu.) |
error |
(error: Error) |
Initialization / runtime error. |
Imperative save (template ref)
<script setup lang="ts">
import { ref } from 'vue';
import { VueOnlyOfficeLocal } from '@zzk-1015/vue-onlyoffice-local';
const editor = ref<InstanceType<typeof VueOnlyOfficeLocal> | null>(null);
async function onSaveClick() {
const result = await editor.value?.save('pdf');
if (result) {
const blob = new Blob([result.buffer], { type: 'application/pdf' });
// download, upload, etc.
}
}
</script>
<template>
<VueOnlyOfficeLocal ref="editor" :file="file" :base-url="'/'" />
<button @click="onSaveClick">Save as PDF</button>
</template>
Low-level SDK access
The vendored oo-offline SDK is also re-exported for advanced users who want to skip the Vue component:
import { createEditor, bufferToBlobUrl } from '@zzk-1015/vue-onlyoffice-local';
const url = bufferToBlobUrl(file, 'docx');
const editor = await createEditor({
container: document.getElementById('host')!,
baseUrl: '/',
document: { url, fileType: 'docx', title: 'demo.docx' },
});
const { buffer } = await editor.save('docx');
editor.destroy();
URL.revokeObjectURL(url);
Slots
| Slot | Scope | Description |
|---|---|---|
loading |
- | Custom content while the editor resources load. |
error |
{ error: Error } |
Custom content if the editor fails to load. |
Migration from 1.x
This release is a behaviour-changing upgrade. Key differences from 1.0.x:
| 1.0.x | 2.0.0 |
|---|---|
External libs/sdk.js + libs/x2t.js |
Self-hosted OnlyOffice 9.x offline vendor build at <baseUrl>/vendor/... |
Conversion via x2t.js WASM + asc_openDocument({buf}) |
Blob URL + Offline.js browser-resident x2t |
Save via legacy onSave callback |
Imperative editor.save(format?) -> SaveResult via postMessage file-stream hook |
sdkUrl, x2tUrl props required |
Both deprecated; baseUrl is the only required URL prop |
Legacy sdkUrl / x2tUrl props are still accepted (ignored) so old callers don't crash, but you must serve the new vendor build for the editor to mount.
Troubleshooting
Every failure below is reported verbatim through the error event / error slot.
| Message | Cause | Fix |
|---|---|---|
Failed to load script: libs/sdk.js |
You are running v1.0.x (this string only exists there; v2 never requests that URL). | Upgrade to 2.0.0 — see Migration from 1.x. |
Failed to load api.js: <url> |
The offline build is missing or one directory level off. <url> is the exact path that 404-ed. |
Make <url> return 200; vendor/ must sit directly under public/. |
DocsAPI not mounted; check baseUrl and the build path |
api.js was fetched (HTTP 200) but did not define window.DocsAPI — usually an HTML error page or SPA fallback served instead of the real file. |
Check the response body in DevTools; fix your dev-server publicDir / rewrite rules. |
| Editor iframe stays blank | The entry api.js loaded but vendor/sdkjs/ is missing. |
Copy the whole vendor/ folder, not just web-apps/. |
Quick check from the browser console:
// must be 200 and start with the Ascensio copyright banner
await fetch(`${baseUrl}vendor/web-apps/apps/api/documents/api.js`).then((r) => r.text())
A failed load is not cached: after fixing the asset path, re-mounting the component issues a fresh request.
Development
Project Setup
npm install
Run Playground (Dev Server)
# 1) Drop the OnlyOffice 9.x offline vendor build into playground/public/vendor/
# (the upstream release ships an html/vendor/ directory - copy it as-is).
# 2) Start the playground:
npm run dev:playground
Build Library
npm run build
License
MIT
Related Packages
Embeddable Vue 3 ticket-format editor with a 1:1 wasm preview. Zero coupling to...
Offline OnlyOffice document editor SDK for React and Vue, powered by WebAssembly
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 2.0.1 | ^3.0.0 | MIT | |
| 2.0.0 | ^3.0.0 | MIT | |
| 1.0.1 | ^3.0.0 | MIT | |
| 1.0.0 | ^3.0.0 | MIT |