Skip to content

Embed the editor

Use @coframe/editor-embed to drop the full Coframe Agent UI into your own app as an iframe. Your app stays in control: send commands (navigate, send a message, toggle read-only) and receive typed events back over a cross-origin postMessage channel — no WebSocket plumbing on your side.

Terminal window
pnpm add @coframe/editor-embed

The <EditorEmbed> component wires up the iframe and the message channel for you:

import { EditorEmbed } from "@coframe/editor-embed/react";
function App() {
return (
<EditorEmbed
editorBaseUrl="https://editor.example.com"
sessionId="my-session"
token="capability-token"
surface="editor"
style={{ width: "100%", height: 600 }}
onEvent={(event) => {
if (event.type === "ready") console.log("Editor ready");
if (event.type === "annotation-submitted") {
console.log("Annotation:", event.submission);
}
}}
/>
);
}

Need to send commands? useEditorEmbed returns an imperative handle:

import { useEditorEmbed } from "@coframe/editor-embed/react";
const embed = useEditorEmbed({
editorBaseUrl: "https://editor.example.com",
sessionId: "my-session",
token: "capability-token",
surface: "editor",
});
embed.navigate("https://example.com/pricing");
embed.sendMessage("Tighten up the copy here");
embed.setReadonly(true);
import { EmbedController } from "@coframe/editor-embed";
const controller = new EmbedController({
editorBaseUrl: "https://editor.example.com",
sessionId: "my-session",
token: "capability-token",
surface: "editor",
});
const iframe = document.createElement("iframe");
iframe.src = controller.buildIframeSrc();
document.body.appendChild(iframe);
controller.attach(iframe);
const unsubscribe = controller.onEvent((event) => {
if (event.type === "ready") console.log("Editor ready");
if (event.type === "annotation-submitted") {
console.log("Annotation:", event.submission);
}
});
// Drive the editor
controller.navigate("https://example.com");
// Later
unsubscribe();
controller.dispose();

The surface option selects which view the iframe renders:

SurfaceShows
editor (default)The full editor UI — chat, preview, and panels.
browserJust the live in-session browser.
annotated-browserThe live browser with the annotation overlay. See Collect visual annotations.

Every message carries a protocol: "coframe:embed/v1" field so you can filter out unrelated postMessage traffic.

CommandSent viaDescription
navigatecontroller.navigate(url)Navigate the live browser to a URL.
messagecontroller.sendMessage(text)Send a chat message to the agent.
set-panelcontroller.setPanel(panel)Switch the active editor panel.
set-readonlycontroller.setReadonly(bool)Toggle read-only mode.
disposecontroller.dispose()Tear down the embed.

onEvent delivers a typed ChildEvent:

event.typePayloadDescription
readysessionId, surfaceHandshake accepted; editor is ready.
auth-errormessageThe capability token was rejected.
session-stateisRunning, isAwaitingApproval, lastSeqLive run state.
browser-statusstatus (starting/ready/expired/error)Live browser lifecycle.
navigatedurlThe browser navigated to a new page.
annotation-submittedsubmissionA user submitted an annotation.
errormessageAn error occurred.

If you wire up window.addEventListener("message", …) yourself, filter events with isChildEvent:

import { isChildEvent } from "@coframe/editor-embed";
window.addEventListener("message", (event) => {
if (!isChildEvent(event.data)) return;
// event.data.type is a known editor event
});
PropTypeDescription
editorBaseUrlstringEditor-web deployment URL.
sessionIdstringSession to connect to.
tokenstringCapability token for the auth handshake.
surfaceEmbedSurface"editor" (default), "browser", or "annotated-browser".
onEvent(event: ChildEvent) => voidReceives typed events from the iframe.
styleCSSPropertiesInline styles for the iframe.
classNamestringClass applied to the iframe.
titlestringAccessible iframe title (default "Editor").

Embed the real Coframe Agent in an iframe and interact via postMessage: