createGatewayReactRoot bootstrap, reading ?runId= and the boot config, live event subscription, node-output reads, a lifecycle action, and an approvals strip, all in one file. Drop it at .smithers/ui/<your-workflow>.tsx, register your workflow with ui: { entry: ".smithers/ui/<your-workflow>.tsx" }, and open it with bunx smithers-orchestrator ui.
For the full conceptual walkthrough, see Custom Workflow UIs.
The bundle
Registering the UI
In your workflow’s gateway setup (thebun .smithers/scripts/<workflow>.ts boot, or your hosted server):
/workflows/demo-react-ui. The HTML shell injects __SMITHERS_GATEWAY_UI__ and ?runId= before your script runs; createGatewayReactRoot picks both up automatically.
Embedding it in apps/smithers
The PWA discovers workflows that advertise hasUi: true and renders this exact bundle in an iframe at /gw/demo-react-ui/<runId>. The same-origin Vite proxy (in dev) and Worker proxy (in Cloud) make the iframe’s RPC and WebSocket reach the real Gateway. No CORS, no token shuttling. See Same-origin proxy patterns.
What this example demonstrates
createGatewayReactRootboot: one call, reads the boot config, mounts the provider, renders the tree.?runId=parsing with a safe fallback to the most recent run viauseGatewayRuns.- Live subscription with
useGatewayRunEvents: resilient reconnect, pushed updates, gap resync, metrics, and automatic teardown whenrunIdchanges. - Node output reads with
useGatewayNodeOutput: handlesproduced/pending/failed. - Approvals strip with
useGatewayApprovals+useGatewayActions().submitApproval. - Lifecycle action with
useGatewayActions().cancelRun. - Stale-data-free transitions: every read and the event stream clears synchronously when
runIdchanges, so a late response from the previous run can never repopulate state. See Stale-data-free update model.