Skip to main content
Workflow UIs are now declared by the workflow itself with <UI>. The UI still runs in the browser, still talks to the Gateway through gateway-react, and can keep the same .smithers/ui/<workflow>.tsx file. The change is ownership: the workflow TSX names the UI entry, so any Gateway that registers the workflow can serve the right UI without out-of-band launcher code.

Before

Older launchers mounted UI from the Gateway side:
.smithers/gateway.ts
gateway.register("release", releaseWorkflow, {
  ui: {
    entry: ".smithers/ui/release.tsx",
    title: "Release",
  },
  entryFile: ".smithers/workflows/release.tsx",
});
Some workspaces relied on the old pack convention where a matching .smithers/ui/release.tsx file was auto-mounted. That convention is replaced by an explicit workflow declaration.

After

Add <UI> inside the workflow’s root <Workflow>:
.smithers/workflows/release.tsx
/** @jsxImportSource smithers-orchestrator */
import { createSmithers, UI } from "smithers-orchestrator";

const { Workflow, Task, smithers } = createSmithers({
  // schemas...
});

export default smithers((ctx) => (
  <Workflow name="release">
    <UI entry="../ui/release.tsx" title="Release" />
    <Task id="ship">{/* normal workflow work */}</Task>
  </Workflow>
));
Then register the workflow without a ui option:
.smithers/gateway.ts
gateway.register("release", releaseWorkflow, {
  entryFile: ".smithers/workflows/release.tsx",
});
entry paths are resolved relative to the workflow file when entryFile is provided. Existing self-mounting UI files that end with createGatewayReactRoot(<App />) can stay as-is.

Entry vs Source

Use entry for existing Gateway UI files:
<UI entry="../ui/release.tsx" title="Release" />
Use source for a browser-safe module that exports a React component. The Gateway mounts it with React and passes props plus boot metadata:
<UI
  source="../ui/release-app.tsx"
  exportName="ReleaseApp"
  title="Release"
  props={{ mode: "operator" }}
/>
Do not point source at the workflow module itself. Workflow modules often import Node/Bun APIs, agents, prompts, and database setup that cannot run in the browser.

Checklist

  1. Add UI to the workflow’s smithers-orchestrator import.
  2. Add <UI entry="../ui/<workflow>.tsx" title="..." /> as a direct child of the root <Workflow>.
  3. Remove ui: { entry, title } from gateway.register; keep entryFile.
  4. Leave the browser UI file in .smithers/ui/<workflow>.tsx unless you want to convert it to the source component-export shape.
  5. Verify with bunx smithers-orchestrator gateway and open /workflows/<workflow> or run bunx smithers-orchestrator ui RUN_ID.
The Gateway discovers <UI> during register(). The component renders nothing into the workflow graph and does not execute client code on the backend.