Vercel AI SDK accessibility
Add paced screen-reader updates to AI SDK through documented state, finish callbacks, and error callbacks.
Install for your existing app
npm install @generative-a11y/react @generative-a11y/ai-sdkRequires AI SDK 7 / @ai-sdk/react 4 and React 18.2+ or 19.
See Compatibility for the supported minor versions and peers.
React integration
Import the hooks
Add these imports to your chat module. In a React Server Components app, keep
it behind a "use client" boundary.
"use client";
import { DefaultChatTransport } from "ai";
import { useChat } from "@ai-sdk/react";
import { A11yProvider, useRuntime } from "@generative-a11y/react";
import {
useChatAccessibility,
useObserveChatAccessibility,
} from "@generative-a11y/ai-sdk/react";Connect your chat
Inside your existing Chat(), create accessibility before useChat, then
observe the returned state. Keep your transport and other chat options; the
example below uses /api/chat.
const runtime = useRuntime();
const accessibility = useChatAccessibility({
runtime,
scopeId: "support",
});
const chat = useChat({
id: "support",
transport: new DefaultChatTransport({ api: "/api/chat" }),
...accessibility.chatCallbacks,
});
useObserveChatAccessibility({ integration: accessibility, snapshot: chat });Add the provider
Wrap your chat once:
export function App() {
return (
<A11yProvider>
<Chat />
</A11yProvider>
);
}Keep using chat in your existing UI. See the
complete copyable component for a minimal
message list and composer.
Existing callbacks
Already use onFinish or onError? Pass your existing functions to
useChatAccessibility so they are composed rather than replaced. Keep the rest
of your options, including transport, in existingOptions, and spread the
composed callbacks last. Omit either callback if your app does not use it.
const accessibility = useChatAccessibility({
runtime,
scopeId: "support",
onFinish,
onError,
});
const chat = useChat({ ...existingOptions, ...accessibility.chatCallbacks });Ownership and behavior
One provider per chat surface
A11yProvider owns runtime and browser delivery. The adapter owns observer
cleanup. Both clean up on unmount; keep the provider mounted across responses
and scopeId stable for that session.
This adds paced live-region updates without changing your visible UI or moving focus during ordinary streaming. Your app still owns semantic structure, keyboard controls, and accessible content. DOM checks do not prove screen-reader output.
Advanced: preconstructed Chat
With useChat({ chat: existingChat }), other initialization options, including
callbacks, are ignored. Compose callbacks when constructing that chat using the
observer API, and manage its subscription and cleanup at that
boundary.
Compatibility
| Dependency | Supported range | Checked version |
|---|---|---|
| Node.js | 22+ | 22.16.0 |
ai | >=7.0.0 <7.1.0 | 7.0.77 |
@ai-sdk/react | >=4.0.0 <4.1.0 | 4.0.80 |
react / react-dom | 18.2+ within major 18, or 19.x | 19.2.8 |
zod (host SDK peer) | 3.25.76+ within major 3, or 4.1.8+ within major 4 | 4.4.3 |
Your existing AI SDK app supplies these dependencies and a working backend.
Core and DOM install transitively; declare them directly only if your own code
imports them. The React hooks live at @generative-a11y/ai-sdk/react; the root
observer entry remains usable without React.
Lifecycle mapping
The adapter translates confirmed public SDK evidence:
| Evidence | Reports |
|---|---|
| Append-only assistant text | Response text updates |
| Tool parts, approval IDs, and source IDs | Tools, approvals, and citations |
Composed onFinish / onError callbacks | Completion, cancellation, failure, and connection loss |
Retry and regenerate detection are unavailable. A successful finish after a disconnection reports recovery. See the observer reference for exact mappings and limits.
Troubleshooting
- Missing completion updates: create accessibility before
useChatand spread its composed callbacks last. - Repeated output: keep
scopeIdstable and attach only one observer per chat. - Existing messages are silent: the first snapshot establishes a history baseline. The adapter observes subsequent updates without replaying history.