React hooks

Open in

AI Transport provides React hooks for building chat UIs. Generic hooks are available from @ably/ai-transport/react and work with any codec. Vercel-specific hooks are available from @ably/ai-transport/vercel/react.

Generic hooks

Import from the React entry point:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

import {
  useClientTransport,
  useView,
  useCreateView,
  useSend,
  useEdit,
  useRegenerate,
  useTree,
  useActiveTurns,
  useAblyMessages,
} from '@ably/ai-transport/react'

useClientTransport

Create and memoize a ClientTransport instance. The transport is created on the first render and the same instance is returned on subsequent renders. The hook does not auto-close the transport on unmount; channel lifecycle is managed by the Ably provider. Call transport.close() explicitly if you need to tear down the transport independently of the channel lifecycle.

JavaScript

1

function useClientTransport(options: ClientTransportOptions): ClientTransport

See ClientTransportOptions for available options.

useView

Subscribe to a view and return the visible node list with pagination, navigation, and write operations. Accepts either a ClientTransport (uses its default view), a View directly, or null/undefined. Returns a ViewHandle that re-renders the component when the view updates.

JavaScript

1

2

3

4

function useView(
  source: ClientTransport | View | null | undefined,
  options?: UseViewOptions
): ViewHandle

UseViewOptions

PropertyTypeDescription
limitnumberMaximum number of older messages to load per page. Defaults to 100.

When options are provided, the hook auto-loads the first page on mount.

ViewHandle

PropertyTypeDescription
messagesTMessage[]The visible domain messages along the selected branch.
nodesMessageNode<TMessage>[]Visible conversation nodes along the selected branch.
send(messages: TMessage | TMessage[], options?: SendOptions) => Promise<ActiveTurn<TEvent>>Send a user message or array of messages.
regenerate(messageId: string, options?: SendOptions) => Promise<ActiveTurn<TEvent>>Regenerate an assistant message, using this view's branch for history.
edit(messageId: string, newMessages: TMessage | TMessage[], options?: SendOptions) => Promise<ActiveTurn<TEvent>>Edit a user message, forking from this view's branch.
update(msgId: string, events: TEvent[], options?: SendOptions) => Promise<ActiveTurn<TEvent>>Amend an existing message and start a continuation turn.
hasOlderbooleanWhether older messages are available.
loadOlder() => Promise<void>Load older messages from history.
loadingbooleanWhether a page load is currently in progress.
getSiblings(msgId: string) => TMessage[]Get all sibling messages at a fork point.
hasSiblings(msgId: string) => booleanWhether a message has sibling alternatives.
getSelectedIndex(msgId: string) => numberIndex of the currently selected sibling at a fork point.
select(msgId: string, index: number) => voidSelect a sibling at a fork point by index. Triggers a view update with the new branch.
getNode(msgId: string) => MessageNode<TMessage> | undefinedGet a node by message ID, or undefined if not found.

useCreateView

Create an additional view of the conversation tree. Returns a ViewHandle with the same interface as useView.

JavaScript

1

function useCreateView(transport: ClientTransport): ViewHandle

useSend

Return a stable send callback bound to the given view. Useful when you only need to send messages without subscribing to the full view.

JavaScript

1

function useSend(view: View): (messages: TMessage[], options?: SendOptions) => Promise<ActiveTurn<TEvent>>

useEdit

Return a stable edit callback bound to the given view.

JavaScript

1

function useEdit(view: View): (messageId: string, newMessages: TMessage | TMessage[], options?: SendOptions) => Promise<ActiveTurn<TEvent>>

useRegenerate

Return a stable regenerate callback bound to the given view.

JavaScript

1

function useRegenerate(view: View): (messageId: string, options?: SendOptions) => Promise<ActiveTurn<TEvent>>

useTree

Provide stable structural query callbacks backed by the transport's conversation tree. These are thin useCallback wrappers around the tree -- no local state or subscriptions. The hook does not re-render on tree changes; use useView for reactive updates.

JavaScript

1

function useTree(transport: ClientTransport): TreeHandle

TreeHandle

PropertyTypeDescription
getSiblings(msgId: string) => TMessage[]Get all sibling messages at a fork point.
hasSiblings(msgId: string) => booleanWhether a message has sibling alternatives.
getNode(msgId: string) => MessageNode<TMessage> | undefinedGet a node by message ID, or undefined if not found.

useActiveTurns

Subscribe to active turns on the channel. Returns a reactive Map keyed by client ID, where each value is a Set of turn IDs for that client. Re-renders when turns start or end.

JavaScript

1

function useActiveTurns(transport: ClientTransport): Map<string, Set<string>>

Use .size to check if any turns are active, .has(clientId) to check a specific client, and .get(clientId) to retrieve the set of turn IDs for a client. See concurrent turns for usage examples.

useAblyMessages

Subscribe to raw Ably messages on the transport's channel. Useful for building custom message handling outside the view abstraction.

JavaScript

1

function useAblyMessages(transport: ClientTransport): Ably.Message[]

Vercel hooks

Import from the Vercel React entry point:

JavaScript

1

import { useChatTransport, useMessageSync } from '@ably/ai-transport/vercel/react'

useChatTransport

Create a ChatTransport for use with Vercel's useChat hook. Accepts either a VercelClientTransportOptions object or an existing ClientTransport instance. Both forms accept an optional second argument for ChatTransportOptions.

JavaScript

1

2

function useChatTransport(options: VercelClientTransportOptions, chatOptions?: ChatTransportOptions): ChatTransport
function useChatTransport(transport: ClientTransport, chatOptions?: ChatTransportOptions): ChatTransport

When you pass VercelClientTransportOptions, the hook creates a new ClientTransport internally with the codec pre-bound to UIMessageCodec. When you pass an existing ClientTransport, it wraps that instance as a ChatTransport without creating a new transport.

Using options (creates transport internally):

JavaScript

1

2

const transport = useChatTransport({ channel })
const { messages } = useChat({ transport })

Using an existing ClientTransport:

JavaScript

1

2

3

const transport = useClientTransport({ channel, codec: UIMessageCodec, clientId })
const chatTransport = useChatTransport(transport)
const { messages } = useChat({ transport: chatTransport })

Use the second form when you need direct access to the ClientTransport for features like active turn tracking or cancellation. See ChatTransportOptions for available options.

useMessageSync

Wire transport message updates into Vercel's useChat message state. Subscribes to the transport view's update event and replaces messages with the view's authoritative message list. Use this when you need Vercel's message array to reflect messages from other devices or browser tabs.

JavaScript

1

2

3

4

function useMessageSync(
  transport: ClientTransport | null | undefined,
  setMessages: (updater: (prev: UIMessage[]) => UIMessage[]) => void
): void
JavaScript

1

2

3

4

5

const transport = useClientTransport({ channel, codec: UIMessageCodec, clientId })
const chatTransport = useChatTransport(transport)
const { messages, setMessages } = useChat({ transport: chatTransport })
useMessageSync(transport, setMessages)
// messages now includes messages from all devices