Web
Serve a browser chat UI from the same Chat SDK bot and handlers you use for Slack, Teams, and Discord. Speaks the AI SDK UI message stream protocol, so useChat works with React, Vue, and Svelte.
Create a new bot
Scaffold a Next.js bot with the Web adapter using create-chat-sdk:
pnpm create chat-sdk@latest my-bot --adapter web memory -yThe generated app uses the Memory state adapter for local development. To add the adapter to an existing app, start at Install.
Install
pnpm add @chat-adapter/web aiThen install the framework package that matches your UI:
| Framework | Package | Import from |
|---|---|---|
| React / Next.js | @ai-sdk/react | @chat-adapter/web/react |
| Vue / Nuxt | @ai-sdk/vue | @chat-adapter/web/vue |
| Svelte / SvelteKit | @ai-sdk/svelte | @chat-adapter/web/svelte |
Quick start
Authentication
The Web adapter has no platform credentials. Browser requests arrive without a platform signature, so your getUser function is the security boundary: it identifies the caller from your own app's session, and returning null rejects the request with HTTP 401.
The resolved user.id is embedded in the Chat SDK thread ID. IDs containing : are rejected with HTTP 400, so normalize them inside getUser (for example, by base64-encoding them) if your auth provider emits IDs like provider:sub.
Configuration
Prop
Type
Streaming
thread.post accepts an AsyncIterable<string | StreamChunk> and writes deltas directly to the SSE response, without the post-and-edit loop or rate limiting other adapters need. You can pass streamText output from the AI SDK:
The adapter honors request.signal, so calling stop() from useChat short-circuits the iterator on the server.
Framework integrations
The Web adapter speaks the AI SDK UI message stream protocol, so React, Vue, and Svelte AI SDK clients work against the same server endpoint. The framework subpaths expose useChat helpers preconfigured for that endpoint. The api and threadId options are the same in all three, and the server-side setup doesn't change.
React
@chat-adapter/web/react is a thin wrapper around @ai-sdk/react's useChat, preconfigured with DefaultChatTransport. It returns destructurable helpers and accepts a few extra options on top of the standard API:
| Option | Description |
|---|---|
api | API endpoint for the Web adapter route. Defaults to /api/chat. |
threadId | Chat SDK thread ID, sent as the request body's id. Recommended. |
experimental_throttle | Throttle wait in ms for chat messages and data updates. |
resume | Whether to resume an ongoing chat generation stream. |
| ...rest | All other options pass through to @ai-sdk/react's useChat. |
For anything the wrapper doesn't cover, use @ai-sdk/react's useChat directly.
Vue / Nuxt
@chat-adapter/web/vue exports a useChat factory that returns a Chat instance from @ai-sdk/vue. Its messages, status, and error properties are Vue-reactive. Access them directly in your template; destructuring breaks Vue's reactivity tracking.
Svelte / SvelteKit
@chat-adapter/web/svelte exports the same factory, returning a Chat instance from @ai-sdk/svelte with Svelte 5 $state-backed reactive properties. As with Vue, the reactive state lives on the object itself.
Message persistence
persistMessageHistory defaults to true. Web has no platform-side history API, so handlers can see prior turns through thread.messages only if the configured state adapter has cached them.
The request body's messages[] array is not an alternative history source. A browser controls it entirely, so the adapter consumes only the latest user message and strips tool parts from it. A client therefore cannot inject forged tool-call or approval state or rewrite earlier turns. Text, file, and custom data-* parts pass through to message.raw unchanged. Setting persistMessageHistory: false leaves handlers with only the current message.
Thread IDs
By default, each useChat conversation maps to one Chat SDK thread:
conversationId is the id field useChat sends in its request body. If your client supplies one (useChat({ id: "support-chat" })), it's reused across reloads; otherwise the adapter generates a fresh ID per request.
Override threadIdFor to use a single thread per user:
The adapter exposes encode and decode helpers:
Feature support
Messaging
| Feature | Supported |
|---|---|
| Post message | |
| Message replies | |
| Edit message | |
| Delete message | |
| File uploads | |
| Streaming | Native (SSE) |
| Scheduled messages |
Rich content
| Feature | Supported |
|---|---|
| Card format | Markdown only (v1) |
| Buttons | |
| Link buttons | |
| Select menus | |
| Tables | GFM |
| Charts | |
| Fields | |
| Images in cards | |
| Modals |
Conversations
| Feature | Supported |
|---|---|
| Slash commands | |
| Mentions | |
| Add reactions | |
| Remove reactions | |
| Typing indicator | |
| Mark as read | |
| Message edit events | |
| Message delete events | |
| DMs | |
| Ephemeral messages | |
| User lookup | |
| Parent subject | |
| Native client | |
| Custom API endpoint |
Message history
| Feature | Supported |
|---|---|
| Fetch messages | State cache |
| Fetch single message | |
| Fetch thread info | Synthesized |
| Fetch channel messages | State cache |
| List threads | |
| Fetch channel info | |
| Post channel message |