useChatTransport
useChatTransport reads a ChatTransport and its underlying ClientSession from the nearest ChatTransportProvider. Pass the returned chatTransport to Vercel AI SDK's useChat({ transport }) and use session for everything else (cancel, tree inspection, raw message access).
The hook is a thin context reader; it does not create or manage any session or transport state. When no provider is found it returns stubs along with a populated sessionError and chatTransportError, and when the provider is found but the session failed to construct it returns a stub session with sessionError set. Either way your UI can render an error state without an error boundary.
1
2
3
4
5
6
7
8
9
10
import { useChat } from '@ai-sdk/react';
import { useChatTransport } from '@ably/ai-transport/vercel/react';
function Chat() {
const { chatTransport, chatTransportError } = useChatTransport();
const { messages, sendMessage } = useChat({ transport: chatTransport });
if (chatTransportError) return <ErrorBanner error={chatTransportError} />;
return <Conversation messages={messages} onSend={sendMessage} />;
}This hook must be used within a ChatTransportProvider (exported from @ably/ai-transport/vercel/react). The provider wraps the subtree in a ClientSessionProvider pre-typed to the Vercel types, so useClientSession and the other core hooks work inside the same subtree.
Parameters
channelNameoptionalStringChatTransportProvider in the tree.skipoptionalBooleantrue, return stubs that throw on any access instead of reading from context.Returns
sessionClientSession<VercelSessionInput, VercelOutput, VercelProjection, UIMessage>skip is true, when no matching provider was found, or when session construction failed.chatTransportChatTransportuseChat. A throwing stub when skip is true or when no matching ChatTransportProvider was found. When the provider was found but the session failed to construct, this is the real transport and sessionError is set instead.sessionErrorAbly.ErrorInfo or UndefinedClientSessionProvider was found or when session construction failed, and skip is false.chatTransportErrorAbly.ErrorInfo or UndefinedChatTransportProvider was found and skip is false.Use the chat transport
chatTransport: ChatTransportThe ChatTransport adapter. Pass it to Vercel AI SDK's useChat({ transport }). It exposes sendMessages, reconnectToStream, close, streaming, and onStreamingChange. The companion useMessageSync hook uses the streaming flag to gate setMessages calls during active own-run streams.
Use the session
session: ClientSession<VercelSessionInput, VercelOutput, VercelProjection, UIMessage>The underlying ClientSession. Use it for operations the chat transport does not expose, for example session.cancel(runId) from a stop button, or session.tree for branch inspection. The same session is available via useClientSession inside the provider's subtree.
Read error state
chatTransportError is set only when no matching ChatTransportProvider is found and skip is false. sessionError covers the wider case: no matching ClientSessionProvider, or a session that failed to construct. A provider that was found but whose session threw during construction gives you sessionError with chatTransportError still undefined. Both are undefined when the handle resolved cleanly, and when skip is true.
Errors the session raises after construction (for example send failures or channel continuity loss) reach you via session.on('error', ...) or via the onError parameter on useClientSession.
Example
A chat component that passes the transport to useChat, with a stop button that cancels the active run through the underlying session.
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
import { useChat } from '@ai-sdk/react';
import { useChatTransport, useMessageSync } from '@ably/ai-transport/vercel/react';
function Chat() {
const { session, chatTransport, chatTransportError } = useChatTransport();
const { messages, setMessages, sendMessage, status } = useChat({
transport: chatTransport,
});
useMessageSync({ setMessages });
if (chatTransportError) return <ErrorBanner error={chatTransportError} />;
return (
<>
{messages.map((m) => <Message key={m.id} message={m} />)}
<Composer
disabled={status !== 'ready'}
onSubmit={(text) => sendMessage({ role: 'user', parts: [{ type: 'text', text }] })}
/>
{status === 'streaming' && (
<button
onClick={() => {
const activeRun = session.view.runs().find((r) => r.status === 'active');
if (activeRun) void session.cancel(activeRun.runId);
}}
>
Stop
</button>
)}
</>
);
}