---
title: Pilot React SDK
description: "React hooks and components for Cuitty Pilot — manage playbooks, runs, and real-time events in React apps."
section: Pilot
order: 12
updatedAt: 2026-06-01
slug: pilot/sdk/react
---
# Pilot React SDK

`@cuitty/pilot-react` provides React hooks and pre-built components for the Pilot API. It wraps `@cuitty/pilot-sdk` with idiomatic React patterns — context providers, data-fetching hooks, and SSE subscriptions that clean up automatically.

## Install

```bash
sfw bun add @cuitty/pilot-react
# or
sfw npm install @cuitty/pilot-react
```

Peer dependencies: `react>=18`, `@cuitty/pilot-sdk`.

## Quick start

```tsx
import { PilotProvider, usePlaybooks, useRun, useRunEvents } from "@cuitty/pilot-react";

function App() {
  return (
    <PilotProvider baseUrl="http://localhost:4320" auth={{ apiKey: "pk_..." }}>
      <Dashboard />
    </PilotProvider>
  );
}

function Dashboard() {
  const { data: playbooks, loading } = usePlaybooks();
  const { run, start } = useRun();
  const events = useRunEvents(run?.id);

  if (loading) return <p>Loading...</p>;

  return (
    <div>
      {playbooks?.map((pb) => (
        <button key={pb.id} onClick={() => start({ playbookId: pb.id, mode: "autonomous" })}>
          {pb.title}
        </button>
      ))}
      {events.map((e, i) => (
        <div key={i}>{e.type} {e.stepId}</div>
      ))}
    </div>
  );
}
```

## PilotProvider

Wraps your component tree with a `PilotClient` instance. Accepts all `PilotClientConfig` props plus `children`. The client is recreated when `baseUrl` or `auth.token` change, and closed on unmount.

```tsx
import { PilotProvider } from "@cuitty/pilot-react";

<PilotProvider
  baseUrl="http://localhost:4320"
  auth={{ token: "ey..." }}
  timeout={15000}
>
  {children}
</PilotProvider>
```

| Prop | Type | Description |
|------|------|-------------|
| `baseUrl` | `string` | Pilot server URL |
| `auth` | `{ token?: string; cookie?: string; apiKey?: string }` | Authentication credentials |
| `timeout` | `number` | Per-request timeout in ms (default 30000) |
| `children` | `React.ReactNode` | Child components |

### usePilot()

Returns the `PilotClient` from context. Throws if used outside `<PilotProvider>`.

```tsx
import { usePilot } from "@cuitty/pilot-react";

const pilot = usePilot();
const health = await pilot.health();
```

## Hooks

### usePlaybooks(options?)

Fetches and caches the playbook list. Re-fetches when options change.

```tsx
const { data, loading, error, refetch } = usePlaybooks();
const { data: cfOnly } = usePlaybooks({ target: "cloudflare" });
```

| Return | Type | Description |
|--------|------|-------------|
| `data` | `PlaybookSummary[] \| null` | Fetched playbooks, `null` until loaded |
| `loading` | `boolean` | `true` while fetching |
| `error` | `Error \| null` | Fetch error, if any |
| `refetch` | `() => Promise<void>` | Manually re-fetch the list |

### useRun()

Manages a single run lifecycle: start, abort, and refresh.

```tsx
const { run, loading, error, start, abort, refresh } = useRun();

// Start a new run
const result = await start({
  playbookId: "cloudflare.dns.add-a-record",
  mode: "interactive",
  inputs: { zone: "example.com", record_name: "api", ip_address: "1.2.3.4" },
});

// Abort the active run
await abort();

// Refresh run state from the server
await refresh();
```

| Return | Type | Description |
|--------|------|-------------|
| `run` | `Run \| null` | Current run object |
| `loading` | `boolean` | `true` while starting a run |
| `error` | `Error \| null` | Error from the last operation |
| `start(req)` | `(StartRunRequest) => Promise<Run \| null>` | Start a run and fetch its full details |
| `abort()` | `() => Promise<void>` | Abort the current run |
| `refresh()` | `() => Promise<void>` | Re-fetch current run state |

### useRunEvents(runId)

Subscribes to real-time SSE events for a run. Automatically unsubscribes on unmount or when `runId` changes. Returns a growing array of events.

```tsx
const events = useRunEvents(run?.id);

// events: RunEvent[]
events.forEach((event) => {
  console.log(event.type, event.stepId);
});
```

| Param | Type | Description |
|-------|------|-------------|
| `runId` | `string \| null \| undefined` | Run ID to subscribe to; `null`/`undefined` pauses subscription |

Returns `RunEvent[]` — accumulates all events received since subscription started.

## Components

### RunStatusBadge

Renders a colored pill badge for a run status.

```tsx
import { RunStatusBadge } from "@cuitty/pilot-react";

<RunStatusBadge status="running" />
<RunStatusBadge status="completed" />
<RunStatusBadge status="failed" />
```

Supported statuses: `queued`, `running`, `awaiting_approval`, `completed`, `failed`, `aborted`.

### RunTimeline

Renders a vertical timeline of run events with color-coded borders (blue for in-progress, green for complete, red for failures).

```tsx
import { RunTimeline } from "@cuitty/pilot-react";

const events = useRunEvents(runId);
<RunTimeline events={events} />
```

### PlaybookCard

Displays a playbook summary card with an optional "Run" button.

```tsx
import { PlaybookCard } from "@cuitty/pilot-react";

<PlaybookCard
  playbook={playbook}
  onRun={(pb) => start({ playbookId: pb.id, mode: "autonomous" })}
/>
```

| Prop | Type | Description |
|------|------|-------------|
| `playbook` | `PlaybookSummary` | Playbook to display |
| `onRun` | `(playbook: PlaybookSummary) => void` | Optional callback when "Run" is clicked |

## Error handling

Errors from hooks are captured in the `error` field rather than thrown. For direct `PilotClient` usage via `usePilot()`, catch `PilotApiError` and `PilotConnectionError` from `@cuitty/pilot-sdk`.

```tsx
import { PilotApiError, PilotConnectionError } from "@cuitty/pilot-sdk";

const pilot = usePilot();
try {
  await pilot.runs.start({ playbookId: "nonexistent" });
} catch (err) {
  if (err instanceof PilotApiError) {
    console.error(`API ${err.status}: ${err.statusText}`, err.body);
  } else if (err instanceof PilotConnectionError) {
    console.error("Cannot reach Pilot:", err.message);
  }
}
```

## See also

- [JavaScript SDK](/docs/pilot/sdk/javascript) — underlying `PilotClient` API reference
- [Playbook reference](/docs/pilot/playbook-reference) — YAML schema for playbook documents
- [Quickstart](/docs/pilot/quickstart) — record and replay your first workflow