Skip to content

Client API & protocol

uframe/embed exposes one function, createUframeEditor, which mounts the iframe and drives it over a small typed postMessage protocol.

createUframeEditor(options)

ts
import { createUframeEditor } from 'uframe/embed'

const editor = createUframeEditor({
  target, // HTMLElement: a container (an <iframe> is created inside) or an existing <iframe>
  src, // URL of the hosted editor app (index.html)
  document, // optional initial PageDocument
  readonly, // optional, default false
  theme, // 'light' | 'dark'
  onReady, // editor mounted and handshaked
  onChange, // (document) => void — fired on edits
  onSave, // (document) => void — fired on explicit save
  onError, // (message) => void
})

Options

OptionTypeNotes
targetHTMLElementWhere to mount. A non-<iframe> element is used as a container (an <iframe> is created inside it); pass an existing <iframe> to drive it directly.
srcstringURL of the hosted editor app.
documentPageDocumentInitial page; omitted → starts empty.
readonlybooleanRender without editing affordances.
theme'light' | 'dark'Initial theme.
pluginsstring[]URLs of plugin dist modules to load + register on ready.
onReady / onChange / onSave / onErrorcallbacksLifecycle + data out.

Returned handle

ts
editor.setDocument(doc) // replace the current document
editor.setReadonly(true) // toggle read-only
editor.setTheme('dark') // switch theme
editor.loadPlugins(['/plugins/callout/dist/index.js']) // load + register plugin dists by URL
editor.requestSave() // ask the editor to emit a `save`
editor.destroy() // remove listeners + iframe
editor.iframe // the underlying <iframe> element

Protocol

The handshake: the iframe posts uframe:ready; the client replies with uframe:load (document + options). Every message is namespaced with a uframe: prefix and carries a numeric v (protocol version) field.

Host → editorEditor → host
uframe:load, uframe:setDocument, uframe:setReadonly, uframe:setTheme, uframe:loadPlugins, uframe:requestSaveuframe:ready, uframe:change, uframe:save, uframe:error

PageDocument is plain JSON, so it travels through postMessage's structured clone unchanged.

Security

  • Serve the editor app from an origin you control and pass it as src.
  • Both sides validate the message origin and source; the host origin is passed to the iframe on its URL so replies are targeted, never *.
  • Allow framing that origin in your host's CSP (frame-src).