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

# React Hooks

> React hooks for the Reactor JavaScript SDK

## `useReactor()`

Selects state from the Reactor store. Uses shallow comparison to avoid unnecessary re-renders.

```typescript Signature theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
useReactor<T>(selector: (state: ReactorStore) => T): T
```

<ParamField path="selector" type="(state: ReactorStore) => T" required>
  A function that picks values from the store.
</ParamField>

```typescript Example theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
// Single value
const status = useReactor((s) => s.status);

// Multiple values
const { status, sendCommand } = useReactor((s) => ({
  status: s.status,
  sendCommand: s.sendCommand,
}));
```

### Store properties

| Property      | Type                                                                    | Description                   |
| ------------- | ----------------------------------------------------------------------- | ----------------------------- |
| `status`      | `ReactorStatus`                                                         | Current connection status     |
| `lastError`   | `ReactorError \| undefined`                                             | Most recent error             |
| `tracks`      | `Record<string, MediaStreamTrack>`                                      | Received tracks by name       |
| `sessionId`   | `string \| undefined`                                                   | Current session ID            |
| `connect`     | `(jwtToken?: string, options?: ConnectOptions) => Promise<void>`        | Connect to the model          |
| `disconnect`  | `(recoverable?: boolean) => Promise<void>`                              | Disconnect                    |
| `reconnect`   | `(options?: ConnectOptions) => Promise<void>`                           | Reconnect to existing session |
| `sendCommand` | `(command: string, data: object) => Promise<void>`                      | Send a command                |
| `uploadFile`  | `(file: File \| Blob, options?: { name?: string }) => Promise<FileRef>` | Upload a file                 |
| `publish`     | `(name: string, track: MediaStreamTrack) => Promise<void>`              | Publish a track               |
| `unpublish`   | `(name: string) => Promise<void>`                                       | Stop publishing a track       |

<Note>
  The store exposes `publish` / `unpublish`; the base `Reactor` class names the same operations
  [`publishTrack`](/sdk-reference/reactor-class#publishtrack) /
  [`unpublishTrack`](/sdk-reference/reactor-class#unpublishtrack). Use whichever matches your entry
  point (hook store vs. class instance).
</Note>

<Note>
  Per-track [`pauseTrack`](/sdk-reference/reactor-class#pausetrack) /
  [`resumeTrack`](/sdk-reference/reactor-class#resumetrack) subscribe to and unsubscribe from an
  output track. They are base `Reactor` methods, not store actions. Reach the instance from a hook
  with `useReactor((s) => s.internal.reactor)` when you need them in React.
</Note>

***

## `useReactorMessage()`

Subscribes to application-level messages from the model. Handles React lifecycle automatically.

```typescript Signature theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
useReactorMessage(handler: (message: any) => void): void
```

<ParamField path="handler" type="(message: any) => void" required>
  Called each time the model sends a message. The shape of `message` depends on the model.
</ParamField>

```typescript Example theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
useReactorMessage((msg) => {
  if (msg.type === "state") {
    setFrame(msg.data.current_frame);
  }
});
```

<Note>
  This hook only receives `application`-scoped messages. Internal platform messages are handled by the SDK.
</Note>

***

## `useStats()`

Returns live WebRTC connection statistics, updated every 2 seconds while connected.

```typescript Signature theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
useStats(): ConnectionStats | undefined
```

```typescript Example theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
const stats = useStats();
if (stats) {
  console.log(`RTT: ${stats.rtt}ms, FPS: ${stats.framesPerSecond}`);
}
```

See [`ConnectionStats`](/sdk-reference/types#connectionstats) for all available fields.

***

## `useClipDownload()`

Headless primitive for downloading a [`Clip`](/sdk-reference/types#clip). Wraps [`reactor.downloadClipAsFile()`](/sdk-reference/reactor-class#downloadclipasfile) and exposes the state of the in-flight download so you can render your own button, progress bar, or menu item.

```typescript Signature theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
useClipDownload(clip: Clip, options?: UseClipDownloadOptions): UseClipDownloadResult
```

<ParamField path="clip" type="Clip" required>
  The clip to download. See [`Clip`](/sdk-reference/types#clip).
</ParamField>

<ParamField path="options.filename" type="string | null" default="&#x22;reactor-clip.mp4&#x22;">
  Filename used by the browser save dialog. Pass `null` to skip the save dialog and just resolve with the `Blob`.
</ParamField>

<ParamField path="options.getJwt" type="() => string | Promise<string>">
  Resolver for the auth token. Required against `https://api.reactor.inc`. See [Authentication](/authentication).
</ParamField>

The hook returns:

| Property   | Type                                                          | Description                                                                                       |
| ---------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `state`    | [`ClipDownloadState`](/sdk-reference/types#clipdownloadstate) | Current state of the download.                                                                    |
| `download` | `() => Promise<Blob \| undefined>`                            | Starts the download. A no-op while one is in flight. Errors are surfaced via `state`, not thrown. |
| `reset`    | `() => void`                                                  | Resets `state` to `idle`. Does not cancel an in-flight download.                                  |

```tsx Example theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
import { useClipDownload } from "@reactor-team/js-sdk";

function Save({ clip, jwt }: { clip: Clip; jwt: string }) {
  const { state, download } = useClipDownload(clip, { getJwt: () => jwt });

  if (state.kind === "downloading") {
    return <progress value={state.fetched} max={state.total || 1} />;
  }
  if (state.kind === "error") {
    return <button onClick={download}>Retry</button>;
  }
  return <button onClick={download}>Save</button>;
}
```

For the default button UI, use [`ClipDownloadButton`](/sdk-reference/react-components#clipdownloadbutton).
