Vercel AI SDK UI

Vercel AI SDK UI gives you client-side React hooks for building chat interfaces. AI Transport implements the transport those hooks use, so the same UI gets durable sessions, multi-device sync, and bidirectional control.

Vercel AI SDK UI is Vercel's React library for building AI chat interfaces. Its useChat hook manages the conversation state, sends user messages, and renders streaming responses. The framework intentionally leaves the transport pluggable through the ChatTransport interface; AI Transport implements that interface and adds the durability that direct HTTP streaming does not provide.

What Vercel AI SDK UI brings

FeatureDescription
useChat hookManages the UIMessage array, the status field (submitted, streaming, ready, error), and the methods sendMessage, regenerate, and stop.
UIMessage modelMessages with parts (text, reasoning, tool calls, tool results, files, sources). Each part tracks its own streaming state.
ChatTransport interfaceThe plug-in point. sendMessages submits messages; reconnectToStream resumes an interrupted stream. The default implementation is HTTP plus Server-Sent Events.
UI componentsPatterns and primitives for chat layouts, message rendering, and input handling.

AI Transport composes with these rather than replacing them; it replaces only the transport useChat uses.

What AI Transport adds

Everything below comes from AI Transport rather than the framework.

FeatureDescriptionAvailable with
Streams that outlive a connectionTokens flow through a channel rather than one HTTP response, so a client can reconnect and pick up where it left off.Streaming
Multi-device deliveryEvery device attached to the conversation receives the same messages in realtime.Streaming
Bidirectional controlCancel, steer, and interrupt the agent from any client, on the same channel as the response.Streaming
Run trackingRun lifecycle events say which client started a run and when it ended. view.runs() keeps the tally for you.Durable session
Conversation branchingEdit and regenerate create forks in the conversation tree rather than destructive replacements.Durable session
Approval gates that reach the user anywherePending tool approvals persist on the session until someone acts on them.Durable session
History and replayPage the channel backwards on reconnect, page refresh, or a new device joining.Streaming
Token compactionReconnecting clients receive accumulated responses rather than a replay of every token.Streaming

Where they connect

AI Transport implements the ChatTransport interface. On the client, swapping it in is a small change to useChat:

JavaScript

1

2

3

4

5

6

7

// Before: default HTTP transport
const { messages } = useChat();

// After: Ably transport (everything else stays the same)
// Wrap your tree with <ChatTransportProvider channelName={chatId}> first.
const { chatTransport } = useChatTransport();
const { messages } = useChat({ transport: chatTransport });

The integration has four parts:

  1. The Vercel codec (from createUIMessageSessionCodec()) encodes Vercel's UIMessageChunk events as Ably messages. The streamed chunk types (text-delta, reasoning-delta and tool-input-delta) append to the single Ably message their stream opened, and the discrete types (start, finish, tool-output-available, tool-output-error and the rest) each publish their own message. Headers carry the metadata for all event types. The codec encodes on the server, decodes on the client, and reassembles chunks into complete UIMessage objects.
  2. ChatTransportProvider creates the underlying ClientSession and the ChatTransport adapter and makes both available through context. useChatTransport is a context reader that returns { session, chatTransport }.
  3. useMessageSync subscribes to the transport's conversation tree and pushes updates into useChat's setMessages. It brings multi-device sync and conversation branching into Vercel's local state, neither of which useChat supports on its own. Pass a messages seed to hydrate from your own database and reconcile that conversation with the live session.
  4. On the server, run.pipe(result.toUIMessageStream()) pipes the model's output through the codec encoder to the session. The transport reads only the response status, so your route can return any body it likes. Tokens reach every connected client through the session rather than through the HTTP response. The server-side integration covers how the agent pipes it.

Typed messages

createUIMessageSessionCodec, createClientSession, createAgentSession, and createChatTransport are generic over the AI SDK's three UIMessage type parameters: message metadata, custom data parts, and tools. Supply them once and your typed message flows through the session, so view.getMessages() returns messages whose metadata, data parts, and tool parts carry your types instead of the SDK defaults. Omit them and type inference is unchanged.

On the client, that looks like this:

JavaScript

1

2

3

4

5

6

7

8

9

10

import { createClientSession } from '@ably/ai-transport/vercel';
import type * as AI from 'ai';

type Metadata = { userId: string };
type DataParts = AI.UIDataTypes & { chart: { points: number[] } };
type Tools = AI.UITools & { getWeather: { input: { city: string }; output: { tempC: number } } };

const session = createClientSession<Metadata, DataParts, Tools>({ client: ably, channelName: 'ai:demo' });
const [{ message }] = session.view.getMessages();
message.metadata; // typed `Metadata | undefined`, not `unknown`

The React provider path (ChatTransportProvider and the hooks from createSessionHooks) stays at the SDK defaults, because those hooks are created once at module scope. To use your own types end-to-end, pass them through the imperative path: createClientSession<Metadata, DataParts, Tools>(), createChatTransport<Metadata, DataParts, Tools>(), and useMessageSync<Metadata, DataParts, Tools>().

Scope and trade-offs

Vercel AI SDK UI is intentionally focused on Vercel's data model. By design, it does not handle multi-device session continuity, branch navigation, or bidirectional control. AI Transport adds all three, and you still use useChat the same way. The same messages array, the same sendMessage and stop methods; the behaviours underneath move from ephemeral HTTP to durable sessions.

If you need direct access to the conversation tree for branch navigation, split-pane views, or custom message construction, use ClientSessionProvider with useView instead of useChat. The useView quickstart builds that path.