← Documentation

Five-minute quickstart

Open a recoverable surface

This path uses the production factory, a typed protocol, strict origin settings, synced state, and the new readiness session.

1. Serve the worker

Copy public/modulon-shared-worker.js from the SDK to the web root. The CLI can scaffold this:

npx modulon init my-app --template react-vite
cd my-app
npm install
npm run dev

2. Create the host

Your host page needs an inline element such as <section id="orders">Orders</section>.

import {
  createModulonHost,
  defineModulonProtocol,
} from "@modulonengine/sdk";

type Methods = {
  "orders.get": {
    params: { id: string };
    result: { id: string; status: string };
  };
};

const isRecord = (v: unknown): v is Record<string, unknown> =>
  !!v && typeof v === "object" && !Array.isArray(v);

const protocol = defineModulonProtocol<Methods>({
  methods: {
    "orders.get": {
      params: (v) => isRecord(v) && typeof v.id === "string",
      result: (v) => isRecord(v) && typeof v.id === "string" && typeof v.status === "string",
    },
  },
});

const runtime = createModulonHost<Methods>({
  appId: "orders-app",
  namespace: "orders-app.v1",
  protocol,
  busSecurity: { mode: "strict" },
  adapterOptions: {
    detachedUrl: "/popout.html",
    detachedUrlParams: { ns: "orders-app.v1" },
    targetOrigin: location.origin,
    allowedOrigins: [location.origin],
  },
  windows: [{ id: "orders", title: "Orders", isDetachable: true }],
  syncedState: { id: "app", initial: { selectedId: "A-100" } },
});

runtime.bus.handle("orders.get", ({ id }) => ({ id, status: "open" }));

document.querySelector("#open")?.addEventListener("click", async () => {
  const opened = await runtime.surfaces.open({ windowId: "orders" });
  if (!opened.ok) {
    console.error(opened.error.code, opened.error.remediation);
    return;
  }
  const ready = await opened.value.ready;
  console.log(ready.ok ? opened.value.snapshot() : ready.error);
});

3. Create the popout route

import { createModulonPopout } from "@modulonengine/sdk";
import { protocol } from "./protocol";

const runtime = createModulonPopout({
  appId: "orders-app",
  namespace: "orders-app.v1",
  protocol,
  busSecurity: { mode: "strict" },
  syncedState: { id: "app" },
});

await runtime.syncState?.ready;
document.body.textContent =
  `Connected ${runtime.ctx.windowId} via ${runtime.surface.transport}`;

4. Verify production behavior

  1. Open from a user click and confirm the session becomes connected.
  2. Refresh the popout and confirm the same session reconnects.
  3. Temporarily disable the worker and confirm BroadcastChannel reports degraded.
  4. Run diagnoseModulon() and export runtime.createSupportBundle().

React

Import ModulonProvider, useSurfaceSession, useSyncedState, and useModulonInspect from @modulonengine/sdk/react. React remains an optional peer and is not included in the core graph.

Open the React Operations Console demo →