Skip to main content
@smithers-orchestrator/gateway-ui is a set of drop-in React components for building a custom workflow UI. They are built on the gateway-react hooks, so each one connects to the Gateway by itself: give it a runId or a filter and it renders live data and wires the actions. Reach for these instead of hand-wiring hooks when you want a run dashboard fast. Import them from the smithers-orchestrator/gateway-ui subpath, alongside createGatewayReactRoot from gateway-react. Every component is styled with inline styles (no CSS import), so it bundles cleanly through the Gateway’s bundler, and every component accepts className and style to override.

Components

ComponentWhat it rendersKey props
RunListA live, selectable list of runs with a status pill per row.filter, activeRunId, onSelect, pollMs
RunTreeThe node tree for a run (initial snapshot plus live updates).runId, onSelectNode, activeNodeId
RunEventLogA scrolling, auto-following event log for a run.runId, maxEvents, follow
NodeOutputViewThe output of a single node, fetched on demand.runId, nodeId, iteration
ApprovalPanelThe pending approval queue with Approve and Deny buttons.filter, pollMs
LaunchButtonA button that launches a workflow run.workflow, input, onLaunched
WorkflowPickerA select of the workflows registered on the Gateway.value, onChange, hasUiOnly
ConnectionBadgeA live Gateway connection indicator.none
StatusPillA colored status badge for any run or node status string.status, label
StatusPill, statusColor, and the theme style tokens are also exported for building your own rows that match the components.

A complete UI

This is a full .smithers/ui/<workflow>.tsx bundle: a run picker, the node tree, a live event log, a node-output pane, and the approval queue, wired together with local state. Save it next to a workflow, register the workflow with ui: { entry: ".smithers/ui/<workflow>.tsx" }, and open it with bunx smithers-orchestrator ui --app or bunx smithers-orchestrator ui RUN_ID.
/** @jsxImportSource react */
import { useState } from "react";
import { createGatewayReactRoot } from "smithers-orchestrator/gateway-react";
import {
  ApprovalPanel,
  ConnectionBadge,
  LaunchButton,
  NodeOutputView,
  RunEventLog,
  RunList,
  RunTree,
  WorkflowPicker,
} from "smithers-orchestrator/gateway-ui";

const WORKFLOW = "implement";

function App() {
  const [runId, setRunId] = useState<string>();
  const [nodeId, setNodeId] = useState<string>();

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 12, padding: 16 }}>
      <header style={{ display: "flex", alignItems: "center", gap: 12 }}>
        <h1 style={{ margin: 0, fontSize: 18 }}>Implement</h1>
        <ConnectionBadge />
        <span style={{ flex: 1 }} />
        <LaunchButton workflow={WORKFLOW} onLaunched={setRunId} />
      </header>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12 }}>
        <RunList filter={{ workflow: WORKFLOW, limit: 20 }} activeRunId={runId} onSelect={setRunId} />
        <RunTree runId={runId} activeNodeId={nodeId} onSelectNode={(node) => setNodeId(node.id)} />
        <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          <NodeOutputView runId={runId} nodeId={nodeId} />
          <RunEventLog runId={runId} style={{ height: 240 }} />
        </div>
      </div>

      <ApprovalPanel filter={{ workflow: WORKFLOW }} />
    </div>
  );
}

createGatewayReactRoot(<App />);
That is the whole UI. RunList polls for new runs, RunTree and RunEventLog stream live as the run advances, ApprovalPanel lets you clear human gates, and LaunchButton starts a fresh run. Mix these with raw gateway-react hooks whenever you need something the components do not cover.

When to drop to hooks

The components own the common shapes. When you need a bespoke layout, an embedded editor, or a custom card, read the same hooks the components use (useGatewayRuns, useGatewayRunTree, useGatewayRunEvents, useGatewayApprovals, useGatewayActions) directly. See Custom Workflow UIs for the full hook walkthrough and Workflow UI (React) for a hand-wired example.