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 or the session failed to construct, the hook returns stubs along with populated sessionError and chatTransportError so the component can render an error state without an error boundary.

JavaScript

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 baked to the Vercel types, so useClientSession and the other core hooks work inside the same subtree.

Parameters

channelNameoptionalString
Look up a specific provider by channel name. Omit to use the innermost ChatTransportProvider in the tree.
skipoptionalBoolean
When true, return stubs that throw on any access instead of reading from context.

Returns

sessionClientSession<VercelInput, VercelOutput, VercelProjection, UIMessage>
The underlying client session. A throwing stub when skip is true, when no matching provider was found, or when session construction failed.
chatTransportChatTransport
The chat transport adapter to pass to Vercel's useChat. A throwing stub in the same conditions as session.
sessionErrorAbly.ErrorInfo or Undefined
Set when no matching ClientSessionProvider was found or when session construction failed, and skip is false.
chatTransportErrorAbly.ErrorInfo or Undefined
Set when no matching ChatTransportProvider was found or when session construction failed, and skip is false.

Use the chat transport

chatTransport: ChatTransport

The 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<VercelInput, 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 when no matching ChatTransportProvider is found, when session construction failed, and skip is false. sessionError is set under the same conditions. Both are undefined when the handle resolved cleanly.

Post-construction errors (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 wired through useChat with a stop button that cancels the active Run through the underlying session.

JavaScript

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>
      )}
    </>
  );
}