> ## Documentation Index
> Fetch the complete documentation index at: https://smithers.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# JSX API

> Author workflows as JSX trees. Smithers renders the tree, dispatches ready tasks, persists outputs, and re-renders. Branching and parallelism are plain JSX conditionals.

Workflows are JSX trees. Smithers renders the tree, extracts ready tasks, executes them, persists outputs, and re-renders. Branching, looping, and parallelism are normal JSX.

<Note>API reference: [Authoring](/reference/authoring) and [Components](/reference/components) list every authoring helper and JSX element, with options and links to source and tests.</Note>

## Setup

Most projects should use `bunx smithers-orchestrator init`; it scaffolds everything below.

To embed into an existing codebase:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
bun add smithers-orchestrator zod
bun add -d typescript @types/react @types/node
```

`smithers-orchestrator` bundles React and ships the JSX runtime at `smithers-orchestrator/jsx-runtime`: skip `react` and `react-dom`. `jsxImportSource` (below) routes JSX through the workflow reconciler, never React DOM. The one required dev dep is `@types/react` (React ships no bundled types; the JSX transform resolves its namespace from it). Only the browser UI surface, `smithers-orchestrator/gateway-react`, takes `react`/`react-dom` as peers.

Minimal `tsconfig.json`:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "jsxImportSource": "smithers-orchestrator",
    "strict": true,
    "noEmit": true,
    "skipLibCheck": true
  }
}
```

`jsxImportSource` is the only non-standard line; it routes JSX through `smithers-orchestrator/jsx-runtime` instead of React DOM.

Optional MDX prompts: add `bun add -d @types/mdx` and a `preload.ts` that calls `mdxPlugin()`, register it in `bunfig.toml` as `preload = ["./preload.ts"]`.

Verify with `bunx tsc --noEmit` and `bunx smithers-orchestrator --help`.

## A minimal workflow

```tsx theme={"theme":{"light":"github-light","dark":"github-dark"}}
// @jsxImportSource smithers-orchestrator (only needed if not set in tsconfig.json)
import { createSmithers, Sequence, Task } from "smithers-orchestrator";
import { z } from "zod";

const { Workflow, smithers, outputs } = createSmithers({
  input: z.object({ repo: z.string() }),
  analysis: z.object({ summary: z.string() }),
});

export default smithers((ctx) => (
  <Workflow name="analyze">
    <Sequence>
      <Task id="analyze" output={outputs.analysis}>
        {{ summary: `Analyze ${ctx.input.repo}` }}
      </Task>
    </Sequence>
  </Workflow>
));
```

`createSmithers` is a named export; the lowercase `smithers` it returns is not a separate top-level import. `smithers((ctx) => ...)` returns the `SmithersWorkflow` value the workflow file exports. The `input` schema above types `ctx.input.repo`; omit it and `ctx.input` is `unknown`, so guard or parse it yourself.

`outputs.analysis` is the typed reference for the Zod schema, so typos are compile errors. The task body here is a JSX expression (`{...}`) returning a plain object: a static return, no LLM call. Real tasks pass a `run` prop or an AI model; see the [Task component reference](/components/workflow).

## Reactivity

The tree re-renders every frame. `ctx` is just an optional function argument: drop it and write `smithers(() => ( ... ))` if a workflow never reads `ctx.input` or `ctx.outputMaybe`. When used, `ctx` exposes `ctx.input` and `ctx.outputMaybe(ref, { nodeId })`, which returns a completed task's output, or `undefined` if it hasn't run yet:

```tsx theme={"theme":{"light":"github-light","dark":"github-dark"}}
const analysis = ctx.outputMaybe(outputs.analysis, { nodeId: "analyze" });
{analysis ? <Task id="report" output={outputs.report} agent={writer}>...</Task> : null}
```

The `report` Task doesn't exist in the plan until `analysis` completes: no placeholder, no skipped node, the conditional IS the dependency. Static DAG tools require declaring optional nodes upfront; the JSX conditional instead evaluates fresh each frame, so `report` simply isn't there while `analysis` is undefined.

For a task that just consumes one upstream output, `<Task deps={{ analyze: outputs.analysis }}>` with a `(deps) => ...` children callback expresses the same gating more directly. Reach for `ctx.outputMaybe` when the logic is structural: branching on content, loops, counts.

## Read next

* [Tour](/tour): six-step worked example with agents, schemas, approvals, resume.
* [How It Works](/how-it-works): the render → execute → persist loop.
* [Components](/components/workflow): full prop surface for every JSX element.
