Skip to main content
import { Branch } from "smithers-orchestrator";

type BranchProps = {
  if: boolean;
  then: ReactElement;
  else?: ReactElement | null;
  skipIf?: boolean;
};
/** @jsxImportSource smithers-orchestrator */
import { createSmithers } from "smithers-orchestrator";
import { z } from "zod";

const { Workflow, Task, Branch, smithers, outputs } = createSmithers({
  input: z.object({
    deployToProduction: z.boolean(),
  }),
  deployment: z.object({
    environment: z.enum(["staging", "production"]),
    url: z.string(),
  }),
});

export default smithers((ctx) => (
  <Workflow name="deploy-pipeline">
    <Branch
      if={ctx.input.deployToProduction}
      then={
        <Task id="deploy-production" output={outputs.deployment}>
          {{ environment: "production", url: "https://prod.example.com" }}
        </Task>
      }
      else={
        <Task id="deploy-staging" output={outputs.deployment}>
          {{ environment: "staging", url: "https://staging.example.com" }}
        </Task>
      }
    />
  </Workflow>
));
Use a plain boolean for if. The example above reads immutable run input, so there is no stale-read hazard: ctx.input.deployToProduction is the same value on every frame of that run. When the condition depends on an upstream task, read with ctx.outputMaybe(...), coalesce the missing first-frame value, and let the render loop re-evaluate after the upstream task persists:
const test = ctx.outputMaybe(outputs.test, { nodeId: "test" });

<Branch
  if={test?.passed === true}
  then={<Task id="deploy" output={outputs.deployment}>...</Task>}
  else={<Task id="notify" output={outputs.notification}>...</Task>}
/>

Notes

  • if re-evaluates every render frame; run input is immutable, and task outputs become visible on later frames after they persist.
  • Read incomplete upstream outputs with ctx.outputMaybe() rather than ctx.output().
  • Each branch takes one element; wrap multiples in <Sequence> or <Parallel>.
  • Unselected branch tasks are absent from the task graph.
  • <Branch> takes no children; pass branches via then/else. Writing <Branch>...</Branch> throws INVALID_INPUT (children would otherwise be silently dropped).