Editor embedding
Embed in 10 minutes
From the DEV Docker bundle to an HTML page that opens, edits and saves a docx file, including how to open your own file through host.docker.internal.
On this page
Goal: in about 10 minutes, an HTML page of yours opens a docx file in the O3O editor, saves it, and you can see the callback the server sends. Everything runs on a DEV machine with the v1 Docker bundle.
Start and check
Start the stack, then call/o3o/status:embed.enabledmust betrueandupstream.coolwsdmust be"ok".Try the demo page
Openhttp://localhost:8080/o3o/demo. It uses the sameapi.js, has a button for each method and an event log.Write your page
Save theindex.htmlbelow into an empty folder.Serve the page
Run a static server on port 3000 and openhttp://localhost:3000. A page onlocalhost, any port, can frame the editor with no extra configuration.Edit, save, see the callback
Type something and press Ctrl+S. The console printsonSaved;/o3o/demo/callbackslists the signature-checkedsavedcallback.
cd online
docker compose --env-file .env -f docker/compose.dev.yml up -d
# Embedding is on and the gate sees the editor server
curl -s http://localhost:8080/o3o/status
# "embed": {"enabled": true, ...} "upstream": {"coolwsd": "ok", ...}<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>O3O.Editor test</title>
<style>html, body { margin: 0; height: 100%; } #o3o-editor { height: 100vh; }</style>
</head>
<body>
<div id="o3o-editor"></div>
<script src="http://localhost:8080/o3o/api.js"></script>
<script>
const editor = new O3O.Editor("o3o-editor", {
document: {
url: "http://localhost:8080/o3o/demo/sample.docx", // sample file shipped when O3O_DEV_MODE=1
title: "sample.docx",
fileType: "docx",
key: "quickstart-1"
},
editor: {
mode: "edit",
lang: "en-US",
user: { id: "dev-1", name: "Developer" },
callbackUrl: "http://o3o-gate:8070/o3o/demo/callback" // the gate's sample callback receiver
},
ui: { closeButton: true },
events: {
onReady: (e) => console.log("onReady", e),
onDocumentLoaded: (e) => console.log("onDocumentLoaded", e),
onModified: (e) => console.log("onModified", e.modified),
onSaved: (e) => console.log("onSaved", e.version, e.url),
onClose: () => document.body.textContent = "Document closed.",
onLimitReached: (e) => console.warn("onLimitReached", e.message),
onError: (e) => console.error("onError", e.code, e.message)
}
});
window.editor = editor; // so you can try editor.save() in the console
</script>
</body>
</html># In the folder that holds index.html
python -m http.server 3000
# Open http://localhost:3000, type something, press Ctrl+S (or run editor.save() in the console), then list the callbacks the gate received:
curl -s http://localhost:8080/o3o/demo/callbacksOpen your own docx file#
document.url is downloaded by the GATE, not the browser. localhost inside a container is that container, and internal addresses are blocked by the SSRF rules. On Docker Desktop your machine is host.docker.internal; add it to O3O_FETCH_ALLOW_HOSTS so the gate may download from it.
# online/.env — let the gate download files from your machine (Docker Desktop)
O3O_FETCH_ALLOW_HOSTS=host.docker.internal
# re-apply to the gate
cd online
docker compose --env-file .env -f docker/compose.dev.yml up -d o3o-gate
# put the file next to index.html and restart the static server (listening on all interfaces)
cp ~/Documents/hop-dong.docx .
python -m http.server 3000 --bind 0.0.0.0document: {
url: "http://host.docker.internal:3000/hop-dong.docx", // the gate downloads it from your machine, not the browser
title: "Sample contract.docx",
fileType: "docx",
key: "hopdong-42-v1" // change it when the source file changes
},Not working? Quick checks#
| Symptom | Cause | Fix |
|---|---|---|
onError: url_not_allowed | The URL points to an internal address that is not allowed | Use host.docker.internal and set O3O_FETCH_ALLOW_HOSTS. |
onError: download_failed | File server down, wrong path, or listening on 127.0.0.1 only | Run with --bind 0.0.0.0; try the URL with curl. |
onError: embed_auth_not_configured | The server has no JWT secret and unsigned mode is off | DEV: O3O_EMBED_ALLOW_UNSIGNED=1. Production: set O3O_EMBED_JWT_SECRET and sign the config. |
onError: origin_not_allowed | The page origin is not in O3O_EMBED_ALLOWED_ORIGINS | Add the origin, e.g. http://localhost:3000. |
| Blank frame, the browser refuses to embed | The page is not on localhost and its origin may not frame the editor | Set O3O_ONLINE_FRAME_ANCESTORS; see errors and limits. |