Skip to content

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.

  1. Start and check

    Start the stack, then call /o3o/status: embed.enabled must be true and upstream.coolwsd must be "ok".
  2. Try the demo page

    Open http://localhost:8080/o3o/demo. It uses the same api.js, has a button for each method and an event log.
  3. Write your page

    Save the index.html below into an empty folder.
  4. Serve the page

    Run a static server on port 3000 and open http://localhost:3000. A page on localhost, any port, can frame the editor with no extra configuration.
  5. Edit, save, see the callback

    Type something and press Ctrl+S. The console prints onSaved; /o3o/demo/callbacks lists the signature-checked saved callback.
BashStep 1 — start
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", ...}
HTMLStep 3 — index.html
<!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>
BashSteps 4 and 5 — serve the page, list callbacks
# 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/callbacks

Open 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.

BashLet the gate download from your machine
# 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.0
JavaScriptReplace the document block in index.html
document: {
  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#

SymptomCauseFix
onError: url_not_allowedThe URL points to an internal address that is not allowedUse host.docker.internal and set O3O_FETCH_ALLOW_HOSTS.
onError: download_failedFile server down, wrong path, or listening on 127.0.0.1 onlyRun with --bind 0.0.0.0; try the URL with curl.
onError: embed_auth_not_configuredThe server has no JWT secret and unsigned mode is offDEV: O3O_EMBED_ALLOW_UNSIGNED=1. Production: set O3O_EMBED_JWT_SECRET and sign the config.
onError: origin_not_allowedThe page origin is not in O3O_EMBED_ALLOWED_ORIGINSAdd the origin, e.g. http://localhost:3000.
Blank frame, the browser refuses to embedThe page is not on localhost and its origin may not frame the editorSet O3O_ONLINE_FRAME_ANCESTORS; see errors and limits.