usePipecatApp
Client bootstrap hook — builds a PipecatClient for a chosen transport and owns the connect/disconnect lifecycle.
Why
Standing up a Pipecat client by hand means choosing and importing a
transport, constructing the client, sequencing device init, and writing the
connect flow — including the server-side bot-start handshake — with correct
error handling and teardown. usePipecatApp is that boilerplate as one
hook. It bootstraps:
- Transport creation — calls your
transportFactory(sync or async), or an app-owned registered loader. Only the package you import is bundled. - Client construction —
new PipecatClient(...)with sensible defaults (mic enabled, camera off), yourclientOptionsmerged over them, and anonClientcallback fired before anything else so early event subscribers miss nothing. - The connect flow — one
connect()that picks the right strategy: bot-start viastartBot()(with response transformation and smallwebrtc ICE-server wiring), the equivalent start-then-connect flow for endpoint params, or a plainconnect(). - Lifecycle guards — connection attempts are ignored outside connectable states (no double connects), teardown disconnects exactly one client even under React StrictMode's double-mounted effects.
- Real error state — transport-load, device-init, and connect failures
all surface in
errorwith their actual messages instead of dying in the console. - Stability — nothing you pass needs memoizing; options are read when used, so inline object literals don't churn the client.
Installation
pnpm dlx shadcn@latest add @pipecat/use-pipecat-appTransport packages are not installed with this hook — they load on demand
so your app only ships the one it uses. Install the package for your
transportType:
| Transport | Install |
|---|---|
smallwebrtc (default) | npm install @pipecat-ai/small-webrtc-transport |
daily | npm install @pipecat-ai/daily-transport |
websocket | npm install @pipecat-ai/websocket-transport |
moq | npm install @pipecat-ai/moq-transport |
Import the installed transport in your app and pass transportFactory. The
registry helper does not reference optional packages, so Vite and Next.js do
not need unused transports installed. A missing package that your app imports
is correctly reported by its bundler.
For a lazy import:
transportFactory: async () => {
const { SmallWebRTCTransport } =
await import("@pipecat-ai/small-webrtc-transport");
return new SmallWebRTCTransport();
};For an app that selects between transport types, register only the loaders it supports before mounting the hook:
import { registerTransport } from "@/lib/transports";
registerTransport("smallwebrtc", async () => {
const { SmallWebRTCTransport } =
await import("@pipecat-ai/small-webrtc-transport");
return SmallWebRTCTransport;
});Calling the hook without a factory or registered loader surfaces a setup error.
Usage
The hook renders nothing — pass the client it returns to a
PipecatClientProvider yourself, and every kit component works beneath it:
"use client";
import { PipecatClientProvider } from "@pipecat-ai/client-react";
import { SmallWebRTCTransport } from "@pipecat-ai/small-webrtc-transport";
import { ConnectButton } from "@/components/pipecat/connect-button";
import { usePipecatApp } from "@/hooks/use-pipecat-app";
import { Spinner } from "@/components/ui/spinner";
export function App() {
const { client, connect, disconnect, error } = usePipecatApp({
transportFactory: () => new SmallWebRTCTransport(),
connectParams: { endpoint: "/api/start" },
});
if (!client) return error ? <p role="alert">{error}</p> : <Spinner />;
return (
<PipecatClientProvider client={client}>
<ConnectButton onConnect={connect} onDisconnect={disconnect} />
{error && <p role="alert">{error}</p>}
</PipecatClientProvider>
);
}connect() picks its flow from the options: with startBotParams it calls
client.startBot() first (optionally transforming the response, and wiring
iceConfig.iceServers onto a smallwebrtc transport), an endpoint-shaped
connectParams starts a bot and connects after checking the client is still active, and anything else
goes straight to client.connect().
The client is created once per transportType — every other option is
read when it's used, so nothing you pass needs memoizing and changing
connectParams between attempts just works. The trade-off: transportOptions
, transportFactory, and clientOptions are read once at creation. To apply a change there,
remount the calling component with a React key.
Key options
| Option | Type | Description |
|---|---|---|
transportType | "smallwebrtc" | "daily" | "websocket" | "moq" | Transport backing the client (default "smallwebrtc"). The only option that rebuilds the client. |
transportFactory | TransportFactory | Creates the transport; may return a promise. Read once at client creation. |
transportOptions | TransportOptions | Transport constructor options, read once at creation. |
clientOptions | Partial<PipecatClientOptions> | Merged into the client constructor (defaults: mic on, cam off). |
connectParams | TransportConnectionParams | APIRequest | Connection params, or an endpoint object for start-and-connect. |
startBotParams | APIRequest | When set, connect() starts the bot first and connects with the response. |
startBotResponseTransformer | (r) => r | Promise<r> | Transforms the startBot response before connecting. |
connectOnMount | boolean | Connect as soon as the client is ready (default false). |
initDevicesOnMount | boolean | Run client.initDevices() once the client exists (default false). |
onClient | (client) => void | Fired once per created client, before device init or connect. |
Returns
| Field | Type | Description |
|---|---|---|
client | PipecatClient | null | Null until the transport loads and the client is built. |
connect | () => Promise<void> | Starts the session; no-op unless state is initialized, disconnected, or error. |
disconnect | () => Promise<void> | Ends the session. |
error | string | null | Real failure message from transport load, device init, or connect. |
clearError | () => void | Manually dismiss error (retrying connect() also clears it). |
rawStartBotResponse | unknown | Latest client.startBot() response. |
transformedStartBotResponse | unknown | That response after the transformer ran. |