---
title: JSX Quickstart
description: Build and run your first Smithers workflow with the JSX API.
---

This guide builds a two-step [workflow](/concepts/workflows-overview) that researches a topic and then writes a report once the research output exists.

It uses the [AI SDK](https://ai-sdk.dev) with [Anthropic](https://docs.anthropic.com) and validates task outputs with [Zod](https://zod.dev).

## Step 1: Create the Workflow

Create `workflow.tsx`:

```tsx
/** @jsxImportSource smithers-orchestrator */
import { createSmithers, Sequence, Task } from "smithers-orchestrator";
import { ToolLoopAgent as Agent } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";

const { Workflow, smithers, outputs } = createSmithers({
  research: z.object({
    summary: z.string(),
    keyPoints: z.array(z.string()),
  }),
  output: z.object({
    title: z.string(),
    body: z.string(),
  }),
});

const researcher = new Agent({
  model: anthropic("claude-sonnet-4-20250514"),
  instructions: "You are an expert research assistant.",
});

const writer = new Agent({
  model: anthropic("claude-sonnet-4-20250514"),
  instructions: "You are a concise technical writer.",
});

export default smithers((ctx) => {
  const research = ctx.outputMaybe(outputs.research, { nodeId: "research" });

  return (
    <Workflow name="research-report">
      <Sequence>
        <Task id="research" output={outputs.research} agent={researcher}>
          {`Research the following topic and return a summary with key points.\n\nTopic: ${ctx.input.topic}`}
        </Task>

        {research ? (
          <Task id="report" output={outputs.output} agent={writer}>
            {`Write a concise report.\n\nSummary: ${research.summary}\nKey points: ${research.keyPoints.join(", ")}`}
          </Task>
        ) : null}
      </Sequence>
    </Workflow>
  );
});
```

Two JSX-specific details matter here:

- `ctx.outputMaybe(outputs.research, { nodeId: "research" })` is how the second render discovers that `research` has finished; see [Workflow State](/concepts/workflow-state) for the persisted lookup model.
- the `report` [`<Task>`](/components/task) only mounts once the `research` output exists.

## Step 2: Run It

Create `main.ts`:

```ts
import { runWorkflow } from "smithers-orchestrator";
import workflow from "./workflow";

const result = await runWorkflow(workflow, {
  input: { topic: "The history of the Zig programming language" },
});

console.log(result.status);
if (result.status === "finished") {
  console.log(JSON.stringify(result.output, null, 2));
}
```

Run it:

```bash
bun run main.ts
```

Or run the workflow directly with the [CLI](/cli/quickstart):

```bash
bunx smithers-orchestrator up workflow.tsx --input '{"topic":"The history of the Zig programming language"}'
```

## What Happened

1. Smithers rendered the JSX tree. Only `research` was mounted; see [Render Frame](/runtime/render-frame) for the render step in detail.
2. The `research` task ran, validated its output against the Zod schema, and persisted it.
3. Smithers rendered again with that stored output available through `ctx.outputMaybe(...)`; see [Reactivity](/concepts/reactivity) for how new tasks mount on later renders.
4. The `report` task mounted on the second render and used the `research` output in its prompt.

## Next Steps

- [JSX Overview](/jsx/overview) — See how JSX rendering, branching, and composition work.
- [Workflow](/components/workflow) — Learn the root workflow component.
- [Task](/components/task) — See agent, compute, and static task modes.
- [runWorkflow](/runtime/run-workflow) — Learn the programmatic runtime entry point used in `main.ts`.
- [Tutorial: Build a Workflow](/guides/tutorial-workflow) — Build a larger production-style JSX workflow.
