Vercel AI SDK accessibility
Add paced screen-reader updates to AI SDK 7 and @ai-sdk/react 4 through documented state, finish callbacks, and error callbacks.
Install the AI SDK adapter
Install @generative-a11y/core, @generative-a11y/dom, and @generative-a11y/ai-sdk beside the AI SDK packages already used by your application. The adapter translates public chat state and callbacks; it does not replace useChat or render a chat interface.
- Use the React subpath when your application connects through @ai-sdk/react.
- Create one accessibility runtime for the chat surface and dispose it when that surface unmounts.
React integration
Create the accessibility integration before useChat so composed callbacks are present at construction, then observe its documented snapshot after useChat runs.
import { useChat } from "@ai-sdk/react";
import {
useChatAccessibility,
useObserveChatAccessibility,
} from "@generative-a11y/ai-sdk/react";
const accessibility = useChatAccessibility({
runtime,
scopeId: "support-thread",
onFinish: hostOnFinish,
onError: hostOnError,
});
const chat = useChat({ id: "support-thread", ...accessibility.chatCallbacks });
useObserveChatAccessibility({ integration: accessibility, snapshot: chat });How this code works
- 01Create accessibility first
The hook creates one observer and composed callbacks before useChat captures its initial options.
- 02Keep your callbacks
The integration observes onFinish and onError, then calls the callbacks supplied by your app.
- 03Construct useChat
Spreading chatCallbacks tells the adapter when a response completes, stops, or fails.
- 04Observe public state
The second hook reads only messages, status, and error from the returned useChat helpers.
Options and return values
runtimePick<GenerativeA11yRuntime, 'dispatch'>Required
Receives events from the integration. Your app owns this runtime, so the integration never disposes it.
- Default
none
scopeIdstringRequired
A stable non-empty namespace for message, tool, approval, and source IDs in this chat instance.
- Default
none
maxTrackedEntitiesnumberOptional
Bounds each identity collection. Invalid values throw and saturation suppresses later unknown identities.
- Default
1000
getToolLabel(context) => stringOptional
Maps public tool identity to short localized copy. It must not expose arguments or raw results.
- Default
"A tool"
onFinishChatOnFinishCallbackOptional
Your existing finish callback. It is composed rather than replaced.
- Default
undefined
onErrorChatOnErrorCallbackOptional
Your existing error callback. Raw errors are not copied into announcements.
- Default
undefined
chatCallbacks{ onFinish, onError }Return
Spread these callbacks into useChat so the adapter knows exactly when a response finishes or fails.
- Default
n/a
snapshotPick<UseChatHelpers, 'messages' | 'status' | 'error'>Required
The public useChat return object observed after useChat has been invoked.
- Default
none
Lifecycle mapping
The observer maps documented message parts and chat status to response, text, tool, approval, and citation events. Composed callbacks confirm completion and failure. Your application must report retry actions because public AI SDK state does not identify every retry request.
- messages and status identify active response text and known tool states
- onFinish confirms completion or stop details
- onError confirms failure without copying raw error text
Expected screen-reader behavior
Core groups streaming text into paced announcement intents and gives approval, failure, and completion updates suitable priority. The DOM package writes those intents to live regions without moving focus during routine status changes. Browser transcripts confirm DOM delivery; test VoiceOver, NVDA, or another target screen reader before making support claims.
What the adapter can report
Use supplied callbacks so the adapter knows when a response finishes, stops, or fails. Status alone does not provide enough detail. Report regenerate actions from your app when you need retry events. A successful response after an error tells the adapter that the connection returned.
- Exports CHAT_ADAPTER_METADATA, createObserver, composeChatCallbacks
- React subpath exports useChatAccessibility and useObserveChatAccessibility
- Tool failure, approvals, and citations use stable public part IDs
Troubleshooting
Missing completion updates often mean useChat captured callbacks before the accessibility integration created them. Repeated output can indicate an unstable scopeId or more than one observer attached to the same chat. Inspect runtime diagnostics, then compare the observed public state with the lifecycle mapping above.
- Create the integration before constructing useChat.
- Keep
scopeIdstable for the lifetime of one chat instance. - Use the lifecycle lab to inspect deterministic browser updates before testing assistive technology.