ChatTransportProvider

ChatTransportProvider is the entry point for the Vercel React integration. It wraps children in a ClientSessionProvider bound to UIMessageCodec, 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 only when chatOptions changes.

JavaScript

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

ChatTransportProvider also re-exports the Vercel-baked core hooks (useClientSession, useView, useTree, useCreateView, useAblyMessages, and the underlying ClientSessionProvider) from the same entry point, all pre-bound to the Vercel TInput / TOutput / TProjection / TMessage types.

Props

ChatTransportProviderProps extends ClientSessionProviderProps with the codec field omitted (always UIMessageCodec) and adds four transport-owned options for the agent-invocation POST.

channelNameString
The channel the session subscribes to. Used as the registry key for nested providers.
apiString
Endpoint the chat transport POSTs the invocation to, to wake the agent. Defaults to /api/chat.
credentialsRequestCredentials
Fetch credentials mode for the invocation POST. Set to 'include' for cookie-based cross-origin auth.
fetchtypeof globalThis.fetch
Custom fetch implementation for the invocation POST. Defaults to globalThis.fetch.
chatOptionsChatTransportOptions
Hooks for customising chat request construction (for example prepareSendMessagesRequest). Must be stable across renders; wrap in useMemo or define outside the component. A new object reference recreates the ChatTransport.
clientIdString
The client's identity, used as the Ably publisher clientId on everything the session publishes.
messagesUIMessage[]
Initial messages to seed the conversation tree with.
loggerLogger
Logger instance for diagnostic output.
childrenReactNode
Descendant components that consume the chat transport via useChatTransport.

Behaviour

useChatTransport and the re-exported useClientSession / useView / useTree / useCreateView / useAblyMessages find the nearest provider in the tree. Pass channelName to look up a specific provider by name when nesting multiple.

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.

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 via useClientSession.sessionError and useChatTransport.chatTransportError. The component tree does not crash.

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 record so descendants can address any registered session by name.

JavaScript

1

2

3

4

5

<ChatTransportProvider channelName="ai:main">
  <ChatTransportProvider channelName="ai:aux">
    <App />
  </ChatTransportProvider>
</ChatTransportProvider>

Inside App:

JavaScript

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

Full Vercel chat wiring using ChatTransportProvider, useChatTransport, and useChat from @ai-sdk/react.

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

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