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

# Hooks

> React hooks from the Pipecat React SDK for client access, media device management, and bot event handling, like usePipecatClient.

The Pipecat React SDK provides hooks for accessing client functionality, managing media devices, and handling events.

## usePipecatClient

Provides access to the `PipecatClient` instance originally passed to `PipecatClientProvider`. Returns `PipecatClient | undefined`, which is `undefined` outside a provider.

```jsx theme={null}
import { usePipecatClient } from "@pipecat-ai/client-react";

function StartButton() {
  const pcClient = usePipecatClient();

  const start = async () => {
    await pcClient?.startBotAndConnect({
      endpoint: "/api/start",
      requestData: {
        // Any custom data your /start endpoint requires
      },
    });
  };

  return <button onClick={start}>Start</button>;
}
```

## useRTVIClientEvent

Allows subscribing to RTVI client events. It is advised to wrap handlers with `useCallback`.

```jsx theme={null}
import { useCallback } from "react";
import { RTVIEvent, TransportState } from "@pipecat-ai/client-js";
import { useRTVIClientEvent } from "@pipecat-ai/client-react";

function EventListener() {
  useRTVIClientEvent(
    RTVIEvent.TransportStateChanged,
    useCallback((transportState: TransportState) => {
      console.log("Transport state changed to", transportState);
    }, [])
  );
}
```

**Arguments**

**event** `RTVIEvent` *required*

**handler** `function` *required*

## usePipecatClientMediaDevices

Manage and list available media devices.

```jsx theme={null}
import { usePipecatClientMediaDevices } from "@pipecat-ai/client-react";

function DeviceSelector() {
  const {
    availableCams,
    availableMics,
    availableSpeakers,
    selectedCam,
    selectedMic,
    selectedSpeaker,
    updateCam,
    updateMic,
    updateSpeaker,
  } = usePipecatClientMediaDevices();

  return (
    <>
      <select
        name="cam"
        onChange={(ev) => updateCam(ev.target.value)}
        value={selectedCam?.deviceId}
      >
        {availableCams.map((cam) => (
          <option key={cam.deviceId} value={cam.deviceId}>
            {cam.label}
          </option>
        ))}
      </select>
      <select
        name="mic"
        onChange={(ev) => updateMic(ev.target.value)}
        value={selectedMic?.deviceId}
      >
        {availableMics.map((mic) => (
          <option key={mic.deviceId} value={mic.deviceId}>
            {mic.label}
          </option>
        ))}
      </select>
    </>
  );
}
```

## useMediaState

Subscribes to the per-device mic and cam state, re-rendering when either transitions. Use it to drive permission UI.

```jsx theme={null}
import { useMediaState } from "@pipecat-ai/client-react";

function MicStatus() {
  const { mic } = useMediaState();

  if (mic.state === "error") {
    return <p>Microphone unavailable: {mic.reason}</p>;
  }
  return <p>Microphone {mic.state}</p>;
}
```

Returns a [`MediaState`](https://docs.pipecat.ai/api-reference/client/js/client-methods#mediastate) snapshot. Safe to call from any component inside `PipecatClientProvider`: the subscription is mounted for you.

## useMediaSupport

Subscribes to which kinds of media can flow in the session, re-rendering when that changes. Use it to hide controls for media the transport or bot doesn't support.

```jsx theme={null}
import { useMediaSupport } from "@pipecat-ai/client-react";

function CamControl() {
  const { cam } = useMediaSupport();

  if (cam === false) return null;
  return <CamToggle />;
}
```

Returns a [`MediaSupport`](https://docs.pipecat.ai/api-reference/client/js/client-methods#mediasupport) snapshot. Safe to call from any component inside `PipecatClientProvider`: the subscription is mounted for you.

## useBotCapabilities

Returns the bot's [`BotCapabilities`](https://docs.pipecat.ai/api-reference/client/js/client-methods#botcapabilities) from its `bot-ready` message, or `undefined` until the bot is ready, after disconnecting, and for bots that don't send capabilities.

```jsx theme={null}
import { useBotCapabilities } from "@pipecat-ai/client-react";

function MetricsPanel() {
  const capabilities = useBotCapabilities();

  if (capabilities?.metrics === false) return null;
  return <Metrics />;
}
```

Safe to call from any component inside `PipecatClientProvider`: the subscription is mounted for you.

## usePipecatClientMediaTrack

Access audio and video tracks.

```jsx theme={null}
import { usePipecatClientMediaTrack } from "@pipecat-ai/client-react";

function MyTracks() {
  const localAudioTrack = usePipecatClientMediaTrack("audio", "local");
  const botAudioTrack = usePipecatClientMediaTrack("audio", "bot");
}
```

**Arguments**

**trackType** `'audio' | 'video' | 'screenAudio' | 'screenVideo'` *required*

**participantType** `'bot' | 'local'` *required*

Returns the `MediaStreamTrack`, or `null` until one has started.

## usePipecatClientAudioElement

Returns a `RefObject<HTMLAudioElement>` to attach to your own `<audio>` element, which then plays the bot's audio track. It is what [`PipecatClientAudio`](https://docs.pipecat.ai/api-reference/client/react/components#pipecatclientaudio) renders: it retries playback on the user's first click, tap, or keypress when the browser blocks autoplay, and follows speaker changes with `setSinkId`.

```jsx theme={null}
import { usePipecatClientAudioElement } from "@pipecat-ai/client-react";

function BotAudio() {
  const audioRef = usePipecatClientAudioElement();
  return <audio ref={audioRef} autoPlay />;
}
```

## usePipecatClientScreenShareControl

Controls the local participant's screen share state.

```jsx theme={null}
import { usePipecatClientScreenShareControl } from "@pipecat-ai/client-react";

function ScreenShareToggle() {
  const { enableScreenShare, isScreenShareEnabled } =
    usePipecatClientScreenShareControl();

  return (
    <button onClick={() => enableScreenShare(!isScreenShareEnabled)}>
      {isScreenShareEnabled ? "Stop Sharing" : "Share Screen"}
    </button>
  );
}
```

Starting a share opens the browser's own picker, so call `enableScreenShare(true)` from a user gesture. Render the result with [`PipecatClientVideo`](https://docs.pipecat.ai/api-reference/client/react/components#pipecatclientvideo) using `trackType="screenVideo"`, or reach the raw track through [`tracks()`](https://docs.pipecat.ai/api-reference/client/js/client-methods#tracks).

## usePipecatClientTransportState

Returns the current transport state.

```jsx theme={null}
import { usePipecatClientTransportState } from "@pipecat-ai/client-react";

function ConnectionStatus() {
  const transportState = usePipecatClientTransportState();
}
```

## usePipecatClientCamControl

Controls the local participant's camera state.

```jsx theme={null}
import { usePipecatClientCamControl } from "@pipecat-ai/client-react";
function CamToggle() {
  const { enableCam, isCamEnabled } = usePipecatClientCamControl();

  return (
    <button onClick={() => enableCam(!isCamEnabled)}>
      {isCamEnabled ? "Disable Camera" : "Enable Camera"}
    </button>
  );
}
```

## usePipecatClientMicControl

Controls the local participant's microphone state.

```jsx theme={null}
import { usePipecatClientMicControl } from "@pipecat-ai/client-react";
function MicToggle() {
  const { enableMic, isMicEnabled } = usePipecatClientMicControl();

  return (
    <button onClick={() => enableMic(!isMicEnabled)}>
      {isMicEnabled ? "Disable Microphone" : "Enable Microphone"}
    </button>
  );
}
```

## useDTMF

Sends DTMF keypresses to the bot: the digits a caller would press on a phone keypad.

```jsx theme={null}
import { useDTMF } from "@pipecat-ai/client-react";

function Keypad() {
  const { sendTone } = useDTMF();

  return (
    <div>
      {["1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "0", "#"].map(
        (key) => (
          <button key={key} onClick={() => sendTone(key)}>
            {key}
          </button>
        ),
      )}
    </div>
  );
}
```

`sendTone` takes a single key or a sequence (`"123#"`), and wraps [`sendDTMF()`](https://docs.pipecat.ai/api-reference/client/js/client-methods#senddtmf), so the same validation and protocol requirements apply. It is a no-op until a client is available from `PipecatClientProvider`.

## usePipecatConversation

The primary hook for accessing the conversation message stream. Returns the current list of messages (ordered for display) and a function to inject messages programmatically.

Each assistant message's text parts are split into `spoken` and `unspoken` segments based on real-time speech progress, so you can style them differently (e.g. dim unspoken text).

```tsx theme={null}
import { usePipecatConversation } from "@pipecat-ai/client-react";
import type {
  BotOutputText,
  ConversationMessage,
} from "@pipecat-ai/client-react";

function Messages() {
  const { messages } = usePipecatConversation({
    onMessageCreated(message: ConversationMessage) {
      console.log("New message:", message);
    },
    onMessageUpdated(message: ConversationMessage) {
      if (message.final) {
        console.log("Message finalized:", message);
      }
    },
  });

  return (
    <ul>
      {messages.map((msg, i) => (
        <li key={`${msg.createdAt}-${i}`}>
          <strong>{msg.role}:</strong>{" "}
          {msg.parts?.map((part, j) => {
            if (typeof part.text === "string") {
              return <span key={j}>{part.text}</span>;
            }
            const { spoken, unspoken } = part.text as BotOutputText;
            return (
              <span key={j}>
                <span>{spoken}</span>
                <span style={{ opacity: 0.5 }}>{unspoken}</span>
              </span>
            );
          })}
        </li>
      ))}
    </ul>
  );
}
```

**Options**

**onMessageCreated** `(message: ConversationMessage) =\> void`

Called once when a new message first enters the conversation. The message may
or may not be complete at this point; check `message.final`.

**onMessageUpdated** `(message: ConversationMessage) =\> void`

Called whenever an existing message's content changes (e.g. streaming text
appended, function call status changed, message finalized). Check
`message.final` to detect finalization.

**outputTypeMetadata** `Record\<string, OutputTypeMetadata\>`

Metadata for output types to control rendering and speech progress behavior.
Used to determine which output types should be excluded from position-based
speech splitting.

**aggregationMetadata** `Record\<string, OutputTypeMetadata\>` *deprecated*

Deprecated. Use `outputTypeMetadata` instead. Where both have an entry for an
output type, the `outputTypeMetadata` entry is used.

**botOutputFilter** `BotOutputFilter`

Restrict which bot output reaches `messages`, as
`{ spoken?: boolean; unspoken?: boolean }`. Use it to render only what the
bot says aloud, or only the text it produces without speaking.

**includeBackchannel** `boolean` (default: false)

Include backchannels: short acknowledgments said while the other side talks,
such as the user's "mhm" or the bot's "Mm-hmm." (RTVI 2.2.0+). Each is its own
message with `backchannel: true`, placed by when it was said, and is also
reported to `onMessageCreated`.

**Returns**

**messages** `ConversationMessage[]`

The current list of conversation messages, ordered for display. Assistant messages have their text parts split into `{ spoken, unspoken }` based on real-time speech progress.

**injectMessage** `(message: \{ role: 'user' | 'assistant' | 'system'; parts: ConversationMessagePart[] \}) =\> void`

Programmatically inject a message into the conversation (e.g. a system prompt or user-typed input).

**sendText** `(text: string, options?: SendTextOptions) =\> Promise\<string\>`

Sends text to the bot and adds it to the conversation as a user message. The message's `status` is `sent`, then `received` once the bot acknowledges it, or `failed` if it couldn't be sent. Bots before RTVI 2.2.0, or with user input turned off, never acknowledge text, so their messages stay `sent`. Use it in place of `injectMessage` plus `client.sendText()`, not alongside them. Resolves to the ID of the send-text message, also the message's `msgId`.

**botOutputEvents** `BotOutputEvent[]`

The raw bot-output events behind `messages`, in arrival order. Useful for
debugging or replaying how a response was assembled.

## useConversationContext

Lower-level hook that provides direct access to the conversation context. Use this when you only need `injectMessage` without subscribing to the message stream, or to check whether the connected bot supports BotOutput events.

```tsx theme={null}
import { useConversationContext } from "@pipecat-ai/client-react";

function TextInput() {
  const { injectMessage, botOutputSupported } = useConversationContext();

  const send = (text: string) => {
    injectMessage({
      role: "user",
      parts: [{ text, final: true, createdAt: new Date().toISOString() }],
    });
  };

  return (
    <input
      onKeyDown={(e) => e.key === "Enter" && send(e.currentTarget.value)}
    />
  );
}
```

**Returns**

**injectMessage** `(message: \{ role: 'user' | 'assistant' | 'system'; parts: ConversationMessagePart[] \}) =\> void`

Programmatically inject a message into the conversation.

**sendText** `(text: string, options?: SendTextOptions) =\> Promise\<string\>`

Sends text to the bot and adds it to the conversation as a user message with a
`status` of `sent`, `received` or `failed`.

**botOutputSupported** `boolean | null`

Whether the connected bot supports BotOutput events (RTVI 1.1.0+). `null`
means detection hasn't completed yet.

## Conversation Utilities

Helpers the conversation store uses, exported for building your own message lists from `ConversationMessage[]`:

| Function | Signature | Description |
| - | - | - |
| `sortByCreatedAt` | `(a: ConversationMessage, b: ConversationMessage) => number` | Comparator that orders messages by `createdAt`. |
| `isMessageEmpty` | `(message: ConversationMessage) => boolean` | Whether a message has no text. Function call messages are never empty. |
| `filterEmptyMessages` | `(messages: ConversationMessage[]) => ConversationMessage[]` | Drops empty messages, keeping one only if no later non-empty message has the same role. |
| `mergeMessages` | `(messages: ConversationMessage[]) => ConversationMessage[]` | Merges a user or assistant message into the previous one of the same role while that one isn't final and is less than 30 seconds older. Text sent with `sendText()` keeps its own message. |
| `deduplicateFunctionCalls` | `(messages: ConversationMessage[]) => ConversationMessage[]` | Keeps one function call message per `tool_call_id`, the one with the most advanced status. |
| `filterBotOutputText` | `(text: BotOutputText, filter?: BotOutputFilter) => BotOutputText` | Applies a `BotOutputFilter`, emptying the `spoken` or `unspoken` portion whose flag is `false`. |

## User Interface

These hooks voice-enable a UI: stream the screen to a server-side [`UIWorker`](https://docs.pipecat.ai/api-reference/server/workers/ui-worker), send UI events, handle the bot's UI commands, and track background work. They must be used within a [`PipecatClientProvider`](https://docs.pipecat.ai/api-reference/client/react/components#pipecatclientprovider). See [the RTVI standard](https://docs.pipecat.ai/api-reference/standards/rtvi-standard.md#user-interface) for wire payloads and [Controlling the UI](https://docs.pipecat.ai/pipecat/learn/ui-worker.md) for the patterns.

### useUISnapshot

Streams accessibility snapshots of the page to the bot. Call once near the root of your app.

```jsx theme={null}
useUISnapshot({ enabled: true, debounceMs: 300, trackViewport: true });
```

**Arguments**: `options?: UseUISnapshotOptions` with `enabled` (default `true`), `debounceMs` (default `300`), `trackViewport` (default `true`), and `logSnapshots` (default `false`).

### useUIEventSender

Returns `(event: string, payload?) => void` to send app-defined UI events to the bot (routed to the worker's `@ui_event` handlers).

```jsx theme={null}
const sendUIEvent = useUIEventSender();
sendUIEvent("note_click", { ref: "e8" });
```

### useUICommandHandler

Registers a handler for a named UI command, active while the component is mounted.

```jsx theme={null}
useUICommandHandler("add_note", (payload) => addNoteToPage(payload));
```

**Arguments**

**command** `string` *required*

**handler** `(payload: T) =\> void | Promise\<void\>` *required*

### Default Command Handlers

`useDefaultUICommandHandlers(options?)` installs DOM handlers for all the standard commands at once (`scroll_to`, `focus`, `highlight`, `select_text`, `set_input_value`, `click`). Each resolves the target by snapshot `ref`, then `target_id`, and refuses unsafe targets. Install them individually with `useDefaultScrollToHandler`, `useDefaultFocusHandler`, `useDefaultHighlightHandler`, `useDefaultSelectTextHandler`, `useDefaultSetInputValueHandler`, and `useDefaultClickHandler`. For the non-DOM commands, `useToastHandler(handler)` and `useNavigateHandler(handler)` are typed convenience wrappers.

```jsx theme={null}
useUISnapshot();
useDefaultUICommandHandlers();
```

**Options**: `useDefaultUICommandHandlers` takes one options object per handler, and each individual hook takes the matching object.

**scrollTo** `DefaultScrollToOptions`

**DefaultScrollToOptions**

**block** `ScrollLogicalPosition` (default: 'start')

`scrollIntoView` block position.

**inline** `ScrollLogicalPosition` (default: 'nearest')

`scrollIntoView` inline position.

**defaultBehavior** `ScrollBehavior` (default: 'smooth')

Scroll behavior when the command's `behavior` is unset.

**container** `Element | null | (() =\> Element | null | undefined)`

Scroll inside this element instead of the nearest scrollable ancestor. The
function form is evaluated on each scroll.

**offset** `\{ top?: number; left?: number \}`

Pixel offsets applied after scrolling, such as to clear a sticky header.
Applied only when `container` is set.

**focus** `DefaultFocusOptions`

**DefaultFocusOptions**

**preventScroll** `boolean` (default: false)

Pass `{ preventScroll: true }` to `element.focus()` so focusing doesn't
scroll the page.

**highlight** `DefaultHighlightOptions`

**DefaultHighlightOptions**

**className** `string` (default: 'ui-highlight')

CSS class toggled on the target for the highlight's duration. Your app
styles it.

**defaultDurationMs** `number` (default: 1500)

Duration when the command's `duration_ms` is missing.

**scrollIntoViewFirst** `boolean` (default: false)

Scroll the target into view before highlighting it.

**selectText** `DefaultSelectTextOptions`

**DefaultSelectTextOptions**

**scrollIntoViewFirst** `boolean` (default: true)

Scroll the target into view before selecting.

**block** `ScrollLogicalPosition` (default: 'center')

`scrollIntoView` block position when scrolling first.

**setInputValue** `DefaultSetInputValueOptions`

**DefaultSetInputValueOptions**

**focusFirst** `boolean` (default: false)

Focus the field before writing, then blur it after the change events
fire.

### useUIJobGroups

Reads accumulated job groups (oldest first) and controls from the nearest [`UIJobGroupsProvider`](https://docs.pipecat.ai/api-reference/client/react/components#uijobgroupsprovider). Without one, it returns no groups and its controls do nothing. Wrap your app in the provider to collect them:

```jsx theme={null}
<UIJobGroupsProvider maxGroups={20}>
  <App />
</UIJobGroupsProvider>
```

```jsx theme={null}
const { groups, cancelJobGroup, dismissJobGroup, clearCompleted } =
  useUIJobGroups();
```

**Returns**: `UIJobGroupsAPI`: `groups: JobGroup[]`, `cancelJobGroup(jobId, reason?)`, `dismissJobGroup(jobId)`, and `clearCompleted()`. Each `JobGroup` has `jobId`, `label`, `cancellable`, `status`, `startedAt`, `completedAt`, and `jobs` (per-worker `{ workerName, status, startedAt, completedAt, updates, response }`).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.