ChatTransportProvider
ChatTransportProvider is the entry point for the Vercel React integration. It wraps children in a ClientSessionProvider bound to the Vercel codec, constructs a ChatTransport over that session, and exposes both through React context. Descendants call useChatTransport to get the transport (and the session) for Vercel useChat.
The Realtime client is read from the surrounding <AblyProvider>. The session is created once on first render. The chat transport is recreated when the session changes, or when any of api, credentials, fetch or chatOptions changes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import * as Ably from 'ably';
import { AblyProvider } from 'ably/react';
import { ChatTransportProvider } from '@ably/ai-transport/vercel/react';
const ably = new Ably.Realtime({ authUrl: '/api/auth/token' });
function App() {
return (
<AblyProvider client={ably}>
<ChatTransportProvider channelName="conversation-42">
<Chat />
</ChatTransportProvider>
</AblyProvider>
);
}The same entry point also exports the core hooks (useClientSession, useView, useTree, useCreateView, useAblyMessages, and the underlying ClientSessionProvider), all pre-typed to the Vercel TInput / TOutput / TProjection / TMessage types.
Props
ChatTransportProviderProps extends ClientSessionProviderProps with the codec field omitted (always the Vercel codec) and adds three transport-owned options for the agent-invocation POST (api, credentials, fetch), plus chatOptions for how each chat request is built. As with ClientSessionProvider, the session's identity is taken from the <AblyProvider> client's auth.clientId (set via the Ably token or ClientOptions.clientId).
channelNameStringchannelModesAbly.ChannelMode[]ClientSessionProvider.apiString/api/chat.credentialsRequestCredentials'include' for cookie-based cross-origin auth.fetchtypeof globalThis.fetchglobalThis.fetch.chatOptionsChatTransportOptionsprepareSendMessagesRequest). Must be stable across renders; wrap in useMemo or define outside the component. A new object reference recreates the ChatTransport.historyPageSizeNumberreorderWindowMsNumberloggerLoggerchildrenReactNodeuseChatTransport.Behaviour
useChatTransport and the re-exported useClientSession / useView / useTree / useCreateView / useAblyMessages find the nearest provider in the React tree. When you nest more than one provider, pass channelName to useChatTransport or useClientSession to look up a specific one by name, and the other hooks take that session as their session option.
The underlying ClientSession is created once on first render via useRef, and connect() runs from a useEffect. The session is closed when the provider truly unmounts.
Because ChatTransportProvider wraps ClientSessionProvider, it also renders an ably-js <ChannelProvider> for the session's channel, so ably-js's usePresence, usePresenceListener, and useChannel hooks work in the subtree for agent presence in React.
The ChatTransport itself is not closed on unmount: its close() delegates to ClientSession.close(), which the inner ClientSessionProvider already calls. Auto-closing here would double-close in React Strict Mode.
If createClientSession throws, the error surfaces as sessionError on both useClientSession and useChatTransport. Your UI can read that error and render an error state. chatTransportError stays undefined in that case, because the provider itself was found.
Nest providers for multiple sessions
Nest providers with distinct channelName values to manage more than one chat in the same tree. Each provider merges its slot into the parent registry so descendants can address any registered session by name.
1
2
3
4
5
<ChatTransportProvider channelName="ai:main">
<ChatTransportProvider channelName="ai:aux">
<App />
</ChatTransportProvider>
</ChatTransportProvider>Inside App:
1
2
3
4
5
6
7
import { useChatTransport } from '@ably/ai-transport/vercel/react';
function App() {
const main = useChatTransport({ channelName: 'ai:main' });
const aux = useChatTransport({ channelName: 'ai:aux' });
// ...
}Example
A full Vercel chat set up with ChatTransportProvider, useChatTransport, and useChat from @ai-sdk/react.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
'use client';
import { useEffect, useState } from 'react';
import * as Ably from 'ably';
import { AblyProvider } from 'ably/react';
import { ChatTransportProvider, useChatTransport } from '@ably/ai-transport/vercel/react';
import { useChat } from '@ai-sdk/react';
function Providers({ children }) {
const [client, setClient] = useState(null);
useEffect(() => {
const ably = new Ably.Realtime({ authUrl: '/api/auth/token', clientId: 'user' });
setClient(ably);
return () => ably.close();
}, []);
if (!client) return null;
return <AblyProvider client={client}>{children}</AblyProvider>;
}
function Chat() {
const { chatTransport } = useChatTransport();
const { messages, sendMessage, status, stop } = useChat({ transport: chatTransport });
return (
<div>
{messages.map((m) => (
<div key={m.id}>{m.role}: {m.parts.map((p) => p.type === 'text' ? p.text : '').join('')}</div>
))}
{status === 'streaming'
? <button type="button" onClick={stop}>Stop</button>
: <button type="button" onClick={() => sendMessage({ text: 'Hello' })}>Send</button>}
</div>
);
}
export default function Page() {
return (
<Providers>
<ChatTransportProvider channelName="my-chat">
<Chat />
</ChatTransportProvider>
</Providers>
);
}