references/ai-sdk.md
# AI SDK migration
Use this reference before an assistant-ui upgrade when the application uses AI SDK v4, v5, or v6. The target integration is @assistant-ui/ai-sdk with ai@^7 and @ai-sdk/react@^4.
## Contents
- [Compatibility and pins](#compatibility-and-pins)
- [v4 and v5 to v6](#v4-and-v5-to-v6)
- [v6 to v7](#v6-to-v7)
- [Current v7 route](#current-v7-route)
- [Approval gates](#approval-gates)
## Compatibility and pins
| AI SDK | assistant-ui integration | Required packages |
| --- | --- | --- |
| v4 | @assistant-ui/react-data-stream | ai@^4 |
| v4, obsolete alternative | @assistant-ui/react-ai-sdk@0.10.16 | AI SDK v4 support ended on that line |
| v5 | @assistant-ui/react-ai-sdk@1.1.21 | ai@^5, @ai-sdk/react@^2, @ai-sdk/openai@^1 |
| v6 | @assistant-ui/react-ai-sdk@1.3.40 | ai@^6, @ai-sdk/react@^3 |
| v7 | @assistant-ui/ai-sdk | ai@^7, @ai-sdk/react@^4 |
The v4, v5, and v6 integrations are legacy and receive no new features. New work targets v7. A v4 application uses useDataStreamRuntime and the data-stream response protocol, not the current AI SDK adapter.
## v4 and v5 to v6
Move v4 through the v5 package APIs before adopting the v6 runtime shape. v4’s data-stream adapter is not a drop-in v6 runtime.
| Area | v4 or v5 | v6 |
| --- | --- | --- |
| AI package | ai@^4 or ai@^5 | ai@^6 |
| React package | v4 has no @ai-sdk/react pin, v5 uses @ai-sdk/react@^2 | @ai-sdk/react@^3 |
| Runtime package | @assistant-ui/react-data-stream or @assistant-ui/react-ai-sdk@1.1.21 | @assistant-ui/react-ai-sdk@1.3.40 |
| Runtime hook | useDataStreamRuntime or useChatRuntime | useChatRuntime |
| Message type | untyped v4 messages or Message | UIMessage |
| Message conversion | synchronous convertToModelMessages in v5 | await convertToModelMessages |
| Tool schema | parameters: z.object({...}) | inputSchema: zodSchema(z.object({...})) |
| Stream response | toDataStreamResponse() | toUIMessageStreamResponse() |
```ts
// Before
const result = streamText({
model,
messages: convertToModelMessages(messages),
tools: {
weather: tool({ parameters: z.object({ city: z.string() }) }),
},
});
return result.toDataStreamResponse();
```
```ts
// After
const result = streamText({
model,
messages: await convertToModelMessages(messages),
tools: {
weather: tool({ inputSchema: zodSchema(z.object({ city: z.string() })) }),
},
});
return result.toUIMessageStreamResponse();
```
AI SDK v6 supports multi-step calls with stopWhen: stepCountIs(n). Without stopWhen, it runs one inference step. The v6 server-side approval model uses a tool-level needsApproval field; replace that approval mechanism when moving to v7.
## v6 to v7
Keep await convertToModelMessages and inputSchema while changing the package majors, package name, response construction, and approval configuration.
| Area | v6 | v7 |
| --- | --- | --- |
| AI packages | ai@^6 and @ai-sdk/react@^3 | ai@^7 and @ai-sdk/react@^4 |
| assistant-ui package | @assistant-ui/react-ai-sdk@1.3.40 | @assistant-ui/ai-sdk |
| Message conversion | await convertToModelMessages(messages) | await convertToModelMessages(messages) |
| Tool schema | inputSchema: zodSchema(z.object({...})) or inputSchema: z.object({...}) | inputSchema: zodSchema(z.object({...})) or inputSchema: z.object({...}) |
| Agent loop | stopWhen: stepCountIs(n) | stopWhen: stepCountIs(n) |
| Response | result.toUIMessageStreamResponse() | createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream }) }) |
| Approval | tool-level needsApproval | call-level toolApproval |
The old adapter package still re-exports the new API for older installs, but v7 source imports from @assistant-ui/ai-sdk.
## Current v7 route
```ts
// After
import { openai } from "@ai-sdk/openai";
import {
convertToModelMessages,
createUIMessageStreamResponse,
stepCountIs,
streamText,
toUIMessageStream,
tool,
zodSchema,
} from "ai";
import { z } from "zod";
export async function POST(request: Request) {
const { messages } = await request.json();
const result = streamText({
model: openai("gpt-5.6-luna"),
messages: await convertToModelMessages(messages),
tools: {
weather: tool({
inputSchema: zodSchema(z.object({ city: z.string() })),
execute: async ({ city }) => ({ city }),
}),
},
stopWhen: stepCountIs(5),
});
return createUIMessageStreamResponse({
stream: toUIMessageStream({ stream: result.stream }),
});
}
```
The v7 frontend imports useChatRuntime from @assistant-ui/ai-sdk. AssistantChatTransport remains the default and forwards system messages and frontend tools to the backend. A custom non-AssistantChatTransport opts out of that forwarding.
## Approval gates
In v7, toolApproval is configured on streamText instead of declaring needsApproval on each tool. The client useChat call needs sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses so it posts the decision back to the route. In a toolkit renderer, approval.approved is undefined while awaiting a choice, true after approval, and false after rejection. Call respondToApproval(response) to answer.
```ts
// After
const result = streamText({
model,
messages: await convertToModelMessages(messages),
tools: {
deploy: tool({
inputSchema: z.object({ target: z.string() }),
execute: async ({ target }) => ({ deployed: target }),
}),
},
toolApproval: {
deploy: (input) =>
input.target === "production" ? "user-approval" : "not-applicable",
},
});
```
For the complete current runtime shape, read [the AI SDK v7 guide](https://www.assistant-ui.com/docs/runtimes/ai-sdk/v7). For legacy pins and behavior, read [the overview](https://www.assistant-ui.com/docs/runtimes/ai-sdk/overview).
references/assistant-ui.md
# assistant-ui migrations
Apply only the sections at or above the application’s installed version, in ascending order. The CLI covers mechanical renames, but it cannot choose application behavior for a custom runtime, renderer, or persistence adapter.
## Contents
- [Version route](#version-route)
- [0.11 ContentPart to MessagePart](#011-contentpart-to-messagepart)
- [0.12 unified state API](#012-unified-state-api)
- [0.14 removals and primitive children](#014-removals-and-primitive-children)
- [0.15 scope properties and removals](#015-scope-properties-and-removals)
- [Tools to toolkits](#tools-to-toolkits)
- [react-langgraph v0.7](#react-langgraph-v07)
- [Deprecation policy](#deprecation-policy)
## Version route
| Release line | Migration action |
| --- | --- |
| 0.8.x | The current CLI excludes the historical v0-8/ui-package-split because its @assistant-ui/react-ui destination is incompatible with current runtimes. Install or move copied UI through the Elements registry instead. |
| 0.9.x | Run the bundled v0-9/edge-package-split codemod. |
| 0.10.x | The supplied migration pages and current bundle have no dedicated 0.10 mapping. Continue to the documented 0.11 migration and resolve remaining package or build fallout. |
| 0.11.x | Rename ContentPart APIs and MessagePrimitive.Content. |
| 0.12.x | Replace assistant API aliases, context hooks, and kebab-case events. |
| 0.13.x | Clear deprecations before 0.14. A 0.13 app without warnings primarily needs the primitive children migration. |
| 0.14.x | Replace removed aliases, runtime APIs, and primitive components props. |
| 0.15.x | Replace scope accessor calls, legacy hooks, tools map, mcp-app, and provider configuration. |
## 0.11 ContentPart to MessagePart
The v0.11 codemod is v0-11/content-part-to-message-part. Replace every type, hook, provider, and primitive from the following mapping.
| Old | New |
| --- | --- |
| TextContentPart | TextMessagePart |
| ReasoningContentPart | ReasoningMessagePart |
| SourceContentPart | SourceMessagePart |
| ImageContentPart | ImageMessagePart |
| FileContentPart | FileMessagePart |
| Unstable_AudioContentPart | Unstable_AudioMessagePart |
| ToolCallContentPart | ToolCallMessagePart |
| ContentPartStatus | MessagePartStatus |
| ToolCallContentPartStatus | ToolCallMessagePartStatus |
| ThreadUserContentPart | ThreadUserMessagePart |
| ThreadAssistantContentPart | ThreadAssistantMessagePart |
| ContentPartRuntime | MessagePartRuntime |
| ContentPartState | MessagePartState |
| useContentPart | useMessagePart |
| useContentPartRuntime | useMessagePartRuntime |
| useContentPartText | useMessagePartText |
| useContentPartReasoning | useMessagePartReasoning |
| useContentPartSource | useMessagePartSource |
| useContentPartFile | useMessagePartFile |
| useContentPartImage | useMessagePartImage |
| useTextContentPart | useMessagePartText |
| EmptyContentPartComponent | EmptyMessagePartComponent |
| TextContentPartComponent | TextMessagePartComponent |
| ReasoningContentPartComponent | ReasoningMessagePartComponent |
| SourceContentPartComponent | SourceMessagePartComponent |
| ImageContentPartComponent | ImageMessagePartComponent |
| FileContentPartComponent | FileMessagePartComponent |
| Unstable_AudioContentPartComponent | Unstable_AudioMessagePartComponent |
| ToolCallContentPartComponent | ToolCallMessagePartComponent |
| EmptyContentPartProps | EmptyMessagePartProps |
| TextContentPartProps | TextMessagePartProps |
| ReasoningContentPartProps | ReasoningMessagePartProps |
| SourceContentPartProps | SourceMessagePartProps |
| ImageContentPartProps | ImageMessagePartProps |
| FileContentPartProps | FileMessagePartProps |
| Unstable_AudioContentPartProps | Unstable_AudioMessagePartProps |
| ToolCallContentPartProps | ToolCallMessagePartProps |
| TextContentPartProvider | TextMessagePartProvider |
| TextContentPartProviderProps | TextMessagePartProviderProps |
| ContentPartRuntimeProvider | MessagePartRuntimeProvider |
| ContentPartContext | MessagePartContext |
| ContentPartContextValue | MessagePartContextValue |
| ContentPartPrimitive | MessagePartPrimitive |
| ContentPartPrimitiveText | MessagePartPrimitiveText |
| ContentPartPrimitiveImage | MessagePartPrimitiveImage |
| ContentPartPrimitiveInProgress | MessagePartPrimitiveInProgress |
| MessagePrimitive.Content | MessagePrimitive.Parts |
MessagePrimitive.Parts now uses its children render function for part-specific rendering. Complete this migration before applying the 0.14 primitive API changes.
## 0.12 unified state API
The v0.12 codemods are v0-12/assistant-api-to-aui, v0-12/event-names-to-camelcase, and v0-12/primitive-if-to-aui-if.
### Core hook aliases
| Old | New |
| --- | --- |
| useAssistantApi | useAui |
| useAssistantState | useAuiState |
| useAssistantEvent | useAuiEvent |
| AssistantIf | AuiIf |
### Removed and deprecated context APIs
This table shows the v0.12 landing form. Apply the 0.15 section afterward to turn the remaining scope calls into properties.
| Old | v0.12 replacement |
| --- | --- |
| useMessageUtils | useAuiState((s) => s.message.isHovering) or useAuiState((s) => s.message.isCopied) |
| useMessageUtilsStore | useAui() with aui.message().setIsHovering() or aui.message().setIsCopied() |
| useToolUIs | Removed with no direct equivalent |
| useToolUIsStore | Removed with no direct equivalent |
| useAssistantRuntime | useAui() |
| useThread | useAuiState((s) => s.thread) |
| useThreadRuntime | useAui().thread() |
| useMessage | useAuiState((s) => s.message) |
| useMessageRuntime | useAui().message() |
| useComposer | useAuiState((s) => s.composer) |
| useComposerRuntime | useAui().composer() |
| useEditComposer | useAuiState((s) => s.message.composer) |
| useThreadListItem | useAuiState((s) => s.threadListItem) |
| useThreadListItemRuntime | useAui().threadListItem() |
| useMessagePart | useAuiState((s) => s.part) |
| useMessagePartRuntime | useAui().part() |
| useAttachment | useAuiState((s) => s.attachment) |
| useAttachmentRuntime | useAui().attachment() |
| useThreadModelContext | useAuiState((s) => s.thread.modelContext) |
| useThreadModelConfig | useAui().thread().getModelContext() |
| useThreadComposer | useAuiState((s) => s.thread.composer) |
| useThreadList | useAuiState((s) => s.threads) |
### Event names
| Old | New |
| --- | --- |
| thread.run-start | thread.runStart |
| thread.run-end | thread.runEnd |
| thread.model-context-update | thread.modelContextUpdate |
| composer.attachment-add | composer.attachmentAdd |
| thread-list-item.switched-to | threadListItem.switchedTo |
| thread-list-item.switched-away | threadListItem.switchedAway |
thread.initialize and composer.send do not change.
## 0.14 removals and primitive children
### Hook aliases
| Removed | Replacement |
| --- | --- |
| useAssistantApi | useAui |
| useAssistantState | useAuiState |
| useAssistantEvent | useAuiEvent |
| AssistantIf | AuiIf |
| useLocalThreadRuntime | useLocalRuntime |
| unstable_useRemoteThreadListRuntime | useRemoteThreadListRuntime |
| unstable_useCloudThreadListAdapter | useCloudThreadListAdapter |
| unstable_RemoteThreadListAdapter | RemoteThreadListAdapter |
| unstable_InMemoryThreadListAdapter | InMemoryThreadListAdapter |
### Runtime APIs
| Removed | Replacement |
| --- | --- |
| runtime.threadList | runtime.threads |
| runtime.switchToNewThread() | runtime.threads.switchToNewThread() |
| runtime.switchToThread(id) | runtime.threads.switchToThread(id) |
| runtime.registerModelConfigProvider(p) | runtime.registerModelContextProvider(p) |
| runtime.reset({ initialMessages }) | runtime.thread.reset(initialMessages) |
| thread.startRun(parentId) | thread.startRun({ parentId }) |
| thread.unstable_resumeRun(config) | thread.resumeRun(config) |
| thread.unstable_loadExternalState(state) | thread.importExternalState(state) |
| thread.getModelConfig() | thread.getModelContext() |
| s.message.submittedFeedback | s.message.metadata.submittedFeedback |
| getExternalStoreMessage(message) | getExternalStoreMessages(message) |
| toAISDKTools(tools) | toToolsJSONSchema(tools) from assistant-stream |
| useLangGraphRuntime({ onSwitchToThread }) | useLangGraphRuntime({ load }) |
toToolsJSONSchema filters disabled and backend tools by default. Pass { filter: () => true } only when the old behavior intentionally included every tool.
### Primitive children render functions
| Deprecated components prop | Children form |
| --- | --- |
| ThreadPrimitive.Messages components | ThreadPrimitive.Messages with ({ message }) => ... |
| MessagePrimitive.Parts components | MessagePrimitive.Parts with ({ part }) => ... |
| ThreadPrimitive.Suggestions components | ThreadPrimitive.Suggestions with () => ... |
| ThreadListPrimitive.Items components | ThreadListPrimitive.Items with () => ... |
| ComposerPrimitive.Attachments components | ComposerPrimitive.Attachments with () => ... |
Return null from MessagePrimitive.Parts to let registered tool and data renderer UIs render. Return an empty fragment to suppress them. Tool-call parts expose toolUI, addResult, and resume directly.
## 0.15 scope properties and removals
The v0-15/aui-accessor-calls-to-properties codemod converts nullary scope calls to properties. aui.thread is always truthy, even when unavailable. Check aui.thread.source != null before accessing an optional scope. source, query, and name are selection metadata on the proxy and never resolve to scope methods.
### Legacy context hooks
| Removed | Replacement |
| --- | --- |
| useAssistantRuntime() | useAui() |
| useThreadList(selector) | useAuiState((s) => s.threads) |
| useThreadRuntime() | useAui().thread |
| useThread(selector) | useAuiState((s) => s.thread) |
| useThreadComposer(selector) | useAuiState((s) => s.thread.composer) |
| useThreadModelContext(selector) | useAuiState((s) => s.thread.modelContext) |
| useMessageRuntime() | useAui().message |
| useMessage(selector) | useAuiState((s) => s.message) |
| useEditComposer(selector) | useAuiState((s) => s.message.composer) |
| useComposerRuntime() | useAui().composer |
| useComposer(selector) | useAuiState((s) => s.composer) |
| useMessagePartRuntime() | useAui().part |
| useMessagePart(selector) | useAuiState((s) => s.part) |
| useAttachmentRuntime() | useAui().attachment |
| useAttachment(selector) | useAuiState((s) => s.attachment) |
| useThreadListItemRuntime() | useAui().threadListItem |
| useThreadListItem(selector) | useAuiState((s) => s.threadListItem) |
useThreadComposerAttachment(Runtime), useEditComposerAttachment(Runtime), and useMessageAttachment(Runtime) are removed with the same attachment mapping.
### Other removed forms
| Old | New |
| --- | --- |
| s.tools.tools[toolName]?.[0] | s.tools.toolUIs[toolName]?.[0]?.render |
| groupPartByType({ "mcp-app": [] }) | groupPartByType({ "standalone-tool-call": [] }) |
| useAui(scopes, { parent }) | useAui() beneath AuiProvider, then AuiConfig(scopes) |
| AuiProvider value={client} | AuiProvider extends={client} config={config} |
| AuiProvider value={null} | AuiProvider extends={null} config={config} |
| useAui({ ... }) | useAui(), then AuiConfig({...}) with provider config |
| AssistantRuntimeProvider aui={aui} | AssistantRuntimeProvider config={config} |
| threadListItem.switchedTo and threadListItem.switchedAway | threads.selectionChanged |
```tsx
// After
const config = AuiConfig({ tools: Tools({ toolkit }) });
<AssistantRuntimeProvider runtime={runtime} config={config}>
{children}
</AssistantRuntimeProvider>;
```
A nested AuiProvider requires extends={aui} to inherit, or extends={null} to isolate. The provider exposes a derived client, not the exact client passed to extends.
threads.selectionChanged carries threadId and previousThreadId. It fires for every listener on the shared threads scope. When reproducing an item-scoped listener, compare threadId with useAuiState((s) => s.threadListItem.id).
Primitive If components, useMessagePartText, useMessagePartReasoning, useMessagePartSource, useMessagePartImage, useMessagePartFile, useMessagePartData, and primitive components props remain deprecated. Use AuiIf, useAuiState with a narrowed part, and children render functions.
## Tools to toolkits
makeAssistantTool, useAssistantTool, makeAssistantToolUI, and useAssistantToolUI are deprecated. A toolkit holds a named tool’s description, parameters, execute, providerOptions, render, renderText, and display in one model contract.
1. Create a "use generative" module exporting defineToolkit({...}).
2. Replace each toolName property with the toolkit object key.
3. Register it once through const config = AuiConfig({ tools: Tools({ toolkit }) }) and runtime provider config={config}.
4. Remove component and hook registration calls.
5. Use externalTool() for a UI-only backend, MCP, or LangGraph renderer. Use stubTool() with useAuiToolOverrides for a stateful executor.
```tsx
// After
"use generative";
import { defineToolkit } from "@assistant-ui/react";
export default defineToolkit({
weather: {
execute: async () => {
"use client";
return { forecast: "sunny" };
},
},
});
```
## react-langgraph v0.7
react-langgraph v0.7 folds thread lifecycle into useLangGraphRuntime.
| Previous pattern | v0.7 pattern |
| --- | --- |
| useCloudThreadListRuntime wrapper | useLangGraphRuntime directly |
| useThreadListItemRuntime().initialize() | initialize passed to stream |
| onSwitchToThread | load |
| onSwitchToNewThread | create |
| Separate runtime hook and wrapper | stream, create, load, delete, and cloud on one hook |
| Manual cloud wrapper | cloud passed to useLangGraphRuntime |
stream receives messages and an object containing abortSignal, initialize, command, runConfig, and checkpointId. initialize resolves remoteId and externalId. create returns { externalId }, and load returns the remote thread state. The threadId and onSwitchToNewThread options are not supported.
## Deprecation policy
Anything marked unstable_, experimental_, or internal, plus RuntimeCore, is experimental and may be removed without notice. Beta APIs have a notice period shorter than one month. They include TailwindCSS plugins, Context API, Runtime API, message types, styled UI components, primitive hooks, attachment APIs, and shadcn/ui styles. Stable primitives, except AttachmentPrimitive, have a notice period longer than three months.
For current decisions, read [the deprecation policy](https://www.assistant-ui.com/docs/migrations/deprecation-policy), then follow any date-specific source deprecation annotation.
references/breaking-changes.md
# Breaking changes quick reference
Use this table to route an error to the full guide. Apply all rows newer than the installed version.
| Version or symptom | Check | Destination |
| --- | --- | --- |
| Before 0.8.x | Historical UI package split is excluded from the current upgrade bundle | Install or move copied components through Elements |
| Before 0.9.x | Edge package split | Run v0-9/edge-package-split |
| Before 0.11.x | ContentPart names or MessagePrimitive.Content | [assistant-ui.md](./assistant-ui.md#011-contentpart-to-messagepart) |
| Before 0.12.x | useAssistantApi, context hooks, or kebab-case events | [assistant-ui.md](./assistant-ui.md#012-unified-state-api) |
| Before 0.14.x | Removed aliases, runtime members, or primitive components props | [assistant-ui.md](./assistant-ui.md#014-removals-and-primitive-children) |
| Before 0.15.x | aui.thread(), legacy hooks, tools map, mcp-app, or old provider props | [assistant-ui.md](./assistant-ui.md#015-scope-properties-and-removals) |
| AI SDK v4 or v5 | Data stream runtime, Message, parameters, or toDataStreamResponse | [ai-sdk.md](./ai-sdk.md#v4-and-v5-to-v6) |
| AI SDK v6 | @assistant-ui/react-ai-sdk, result.toUIMessageStreamResponse(), or needsApproval | [ai-sdk.md](./ai-sdk.md#v6-to-v7) |
| Legacy tool registration | makeAssistantTool, useAssistantTool, makeAssistantToolUI, or useAssistantToolUI | [assistant-ui.md](./assistant-ui.md#tools-to-toolkits) |
| LangGraph v0.7 | useCloudThreadListRuntime, onSwitchToThread, or manual thread initialization | [assistant-ui.md](./assistant-ui.md#react-langgraph-v07) |
| React 18 | A copied shadcn Button does not forward its ref | Wrap Button with React.forwardRef |
| 0.11 types | TextContentPart, ToolCallContentPart, ContentPartStatus, or related names | Replace ContentPart with MessagePart throughout |
| 0.11 hooks | useContentPart, useContentPartRuntime, or useTextContentPart | Use the corresponding useMessagePart API |
| 0.11 providers | ContentPartRuntimeProvider or ContentPartContext | Use the corresponding MessagePart provider or context |
| 0.11 primitives | ContentPartPrimitive | Use MessagePartPrimitive |
| 0.11 message rendering | MessagePrimitive.Content | Use MessagePrimitive.Parts |
| 0.12 state aliases | useAssistantApi or useAssistantState | Use useAui or useAuiState |
| 0.12 event alias | useAssistantEvent | Use useAuiEvent |
| 0.12 conditional alias | AssistantIf | Use AuiIf |
| 0.12 state scope | useThread, useMessage, useComposer, or useAttachment | Select the matching scope through useAuiState |
| 0.12 action scope | useThreadRuntime, useMessageRuntime, or useComposerRuntime | Start with useAui, then apply the 0.15 property form |
| 0.12 event name | thread.run-start, thread.run-end, or composer.attachment-add | Use the camelCase event names |
| 0.14 local runtime | useLocalThreadRuntime | Use useLocalRuntime |
| 0.14 remote thread list | unstable_useRemoteThreadListRuntime | Use useRemoteThreadListRuntime |
| 0.14 thread list adapter | unstable_RemoteThreadListAdapter or unstable_InMemoryThreadListAdapter | Use the stable adapter names |
| 0.14 assistant runtime | runtime.threadList or runtime.switchToThread | Use runtime.threads |
| 0.14 thread runtime | startRun(parentId), unstable_resumeRun, or getModelConfig | Use the object, stable, or model-context form |
| 0.14 feedback state | s.message.submittedFeedback | Read s.message.metadata.submittedFeedback |
| 0.14 external store | getExternalStoreMessage | Use getExternalStoreMessages |
| 0.14 transport helper | toAISDKTools | Use toToolsJSONSchema from assistant-stream |
| 0.14 thread messages | ThreadPrimitive.Messages components prop | Use a children render function |
| 0.14 message parts | MessagePrimitive.Parts components prop | Use a children render function |
| 0.14 suggestions | ThreadPrimitive.Suggestions components prop | Use a children render function |
| 0.14 thread list | ThreadListPrimitive.Items components prop | Use a children render function |
| 0.14 attachments | ComposerPrimitive.Attachments components prop | Use a children render function |
| 0.15 scope access | aui.thread(), aui.threads(), aui.message(), or aui.composer() | Read the scope property, then call its methods |
| 0.15 optional scope | A truthiness check for aui.thread | Check aui.thread.source != null |
| 0.15 legacy hooks | useAssistantRuntime or a context runtime hook | Use useAui or useAuiState with the final property mapping |
| 0.15 tool UI map | s.tools.tools | Use s.tools.toolUIs |
| 0.15 part grouping | mcp-app | Use standalone-tool-call |
| 0.15 provider construction | useAui({ ... }) | Use useAui(), AuiConfig({...}), and config |
| 0.15 provider prop | AuiProvider value | Use extends plus config |
| 0.15 runtime provider | AssistantRuntimeProvider aui | Use AssistantRuntimeProvider config |
| 0.15 thread event | threadListItem.switchedTo or threadListItem.switchedAway | Use threads.selectionChanged |
| 0.15 primitive conditionals | ThreadPrimitive.If, MessagePrimitive.If, or ThreadPrimitive.Empty | Use AuiIf |
| 0.15 part hooks | useMessagePartText, useMessagePartReasoning, or another specialized part hook | Select and narrow s.part with useAuiState |
| 0.15 registry path | @/components/assistant-ui/thread or another retired path | Use @/components/assistant-ui/elements/<name>.aui |
| 0.15 interactables | useAssistantInteractable, Interactables(), or useInteractableState | Use unstable interactables before the 2026-09-14 removal date |
| AI SDK v7 package | @assistant-ui/react-ai-sdk in current source | Import from @assistant-ui/ai-sdk |
| AI SDK v7 route | result.toUIMessageStreamResponse() | Use createUIMessageStreamResponse with toUIMessageStream |
| AI SDK v7 approval | needsApproval | Configure the call-level toolApproval option |
| AI SDK agent loop | maxSteps | Use stopWhen: stepCountIs(n) |
The current target is @assistant-ui/react 0.15.x, @assistant-ui/ai-sdk 0.0.x, ai 7.x, and @ai-sdk/react 4.x. Run npx assistant-ui@latest doctor and npx assistant-ui@latest info when a dependency mismatch remains.
SKILL.md
---
name: update
description: "Upgrades an existing assistant-ui application and applies the migrations needed to reach the current AI SDK v7 and assistant-ui 0.15.x lines. Use when updating, bumping, or migrating @assistant-ui/react, @assistant-ui/ai-sdk, the older @assistant-ui/react-ai-sdk alias, ai, or @ai-sdk/react, or when an upgrade exposes removed hooks, scope accessor calls, provider configuration, registry paths, event names, legacy interactables, or tool registrations. Start here for an existing project, post-update type failures, package compatibility, CLI codemods, and version-specific migration order. For a first install or a new project use setup; for authoring a runtime without an upgrade use runtime."
license: MIT
---
# assistant-ui Update
**Always consult [assistant-ui.com/llms.txt](https://www.assistant-ui.com/llms.txt) for the latest API.**
Upgrade in two passes. First make the AI SDK and its assistant-ui adapter compatible, then update assistant-ui and apply its API migration. Read the relevant references before editing because a direct jump can cross several deprecation windows.
## References
- [./references/ai-sdk.md](./references/ai-sdk.md) -- AI SDK v4 and v5 to v6 migration, v6 to v7 changes, and adapter pins
- [./references/assistant-ui.md](./references/assistant-ui.md) -- assistant-ui 0.8 through 0.15, toolkits, LangGraph, and deprecation policy
- [./references/breaking-changes.md](./references/breaking-changes.md) -- quick version and symptom lookup
## Detect the installed lines
Run these commands from the application root. npm ls reports the installed dependency graph and npm view reports the published latest version.
```bash
npm ls @assistant-ui/react @assistant-ui/ai-sdk @assistant-ui/react-ai-sdk @assistant-ui/core @assistant-ui/store assistant-stream ai @ai-sdk/react
npm view @assistant-ui/react version
npm view @assistant-ui/ai-sdk version
npm view @assistant-ui/react-ai-sdk version
npm view @assistant-ui/core version
npm view @assistant-ui/store version
npm view assistant-stream version
npm view ai version
npm view @ai-sdk/react version
```
Current published lines as of September 2026:
| Package | Current line |
| --- | --- |
| assistant-ui | 0.0.x |
| @assistant-ui/react | 0.15.x |
| @assistant-ui/ai-sdk | 0.0.x |
| @assistant-ui/react-ai-sdk | 1.4.x |
| @assistant-ui/core | 0.3.x |
| @assistant-ui/store | 0.3.x |
| assistant-stream | 0.3.x |
| assistant-cloud | 0.1.x |
| ai | 7.x |
@assistant-ui/react-ai-sdk re-exports the same API for older installs. New code imports from @assistant-ui/ai-sdk.
## Choose the migration set
Compare the installed @assistant-ui/react version against every threshold below. Apply every applicable guide in ascending version order.
| Installed before | Check for |
| --- | --- |
| 0.8.x | The historical UI package split. The current upgrade bundle intentionally excludes v0-8/ui-package-split because its destination is incompatible with current runtimes. Move to the Elements registry manually. |
| 0.9.x | The v0-9/edge-package-split codemod. |
| 0.10.x | The bundled migration has no dedicated 0.10 codemod. Run the later codemods and resolve remaining package or build errors from the project’s current toolchain. |
| 0.11.x | ContentPart names and MessagePrimitive.Content become MessagePart and MessagePrimitive.Parts. |
| 0.12.x | Unified state API, hook aliases, and camelCase event names. |
| 0.13.x | Review the 0.14 guide before proceeding because it removes the v0.11 and v0.12 deprecations. |
| 0.14.x | Removed aliases and runtime APIs, plus primitive children render functions. |
| 0.15.x | Scope properties, removed legacy hooks, toolUIs, standalone-tool-call, AuiConfig, and threads.selectionChanged. |
## Migration order
1. Migrate the AI SDK first. Read [ai-sdk.md](./references/ai-sdk.md) when the project is on v4, v5, or v6. Target ai@^7 and @ai-sdk/react@^4 with @assistant-ui/ai-sdk.
2. Update assistant-ui next. Use the threshold table and [assistant-ui.md](./references/assistant-ui.md), starting with the oldest applicable version.
3. Verify only after the package and source migrations are both complete. Typecheck, build, and exercise chat, tool, approval, and thread-selection paths that the application uses.
## Run the CLI
```bash
# Update every installed @assistant-ui/* package.
npx assistant-ui@latest update
# Preview package changes without installing them.
npx assistant-ui@latest update --dry
# Preview the complete bundled migration and print each transformed file.
npx assistant-ui@latest upgrade -d -p
# Apply one codemod to a source directory.
npx assistant-ui@latest codemod v0-11/content-part-to-message-part ./src
# Report environment and dependency details.
npx assistant-ui@latest doctor
npx assistant-ui@latest info
```
The bundled upgrade command runs these codemods in this exact order:
1. v0-9/edge-package-split
2. v0-11/content-part-to-message-part
3. v0-12/assistant-api-to-aui
4. v0-12/event-names-to-camelcase
5. v0-12/primitive-if-to-aui-if
6. v0-15/aui-accessor-calls-to-properties
Use the dry and print form first. After reviewing the diff, run upgrade without -d and -p. Do not add the historical v0-8/ui-package-split codemod to a current upgrade.
## 0.15.x follow-ups
These changes shipped after 0.15.0 without another major. Sweep for them even if the project already declares 0.15.x.
### Move the AI SDK import
```tsx
// Before
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
```
```tsx
// After
import { useChatRuntime } from "@assistant-ui/ai-sdk";
```
### Replace client construction with configuration
useAui takes no configuration. Build a configuration with AuiConfig and give it to the provider. A nested AuiProvider must declare whether it extends the parent client or is isolated.
```tsx
// Before
const aui = useAui({ tools: Tools({ toolkit }) });
return <AuiProvider value={aui}>{children}</AuiProvider>;
```
```tsx
// After
const aui = useAui();
const config = AuiConfig({ tools: Tools({ toolkit }) });
return <AuiProvider extends={aui} config={config}>{children}</AuiProvider>;
```
At a runtime boundary, replace AssistantRuntimeProvider aui with config. For an isolated root, use AuiProvider extends={null} config={config}.
```tsx
// Before
return <AssistantRuntimeProvider runtime={runtime} aui={aui}>{children}</AssistantRuntimeProvider>;
```
```tsx
// After
const config = AuiConfig({ tools: Tools({ toolkit }) });
return <AssistantRuntimeProvider runtime={runtime} config={config}>{children}</AssistantRuntimeProvider>;
```
### Move copied registry components
Runtime-connected registry components live at components/assistant-ui/elements/<name>.aui.tsx and import as @/components/assistant-ui/elements/<name>.aui. Renderers and standalone Elements use components/assistant-ui/elements/<name>.tsx and omit .aui from their import. Replace retired @/components/assistant-ui/<name> imports during the same sweep.
### Consolidate thread selection events
```tsx
// Before
useAuiEvent("threadListItem.switchedTo", ({ threadId }) => select(threadId));
useAuiEvent("threadListItem.switchedAway", ({ threadId }) => clear(threadId));
```
```tsx
// After
useAuiEvent("threads.selectionChanged", ({ threadId, previousThreadId }) => {
select(threadId);
if (previousThreadId) clear(previousThreadId);
});
```
The new event is shared by the threads scope. A listener that previously lived inside a thread-list item can filter by its item id.
### Replace legacy interactables and tool registrations
useAssistantInteractable, Interactables(), and useInteractableState are deprecated since 2026-06-14 and scheduled for removal on or after 2026-09-14. Migrate to unstable_useInteractable, unstable_Interactables(), and unstable_interactableTool.
makeAssistantTool, useAssistantTool, makeAssistantToolUI, and useAssistantToolUI are deprecated. Put the model contract, executor, and renderer in a defineToolkit entry and register it with AuiConfig({ tools: Tools({ toolkit }) }). Read the toolkits section in [assistant-ui.md](./references/assistant-ui.md) before converting stateful or UI-only tools.
## Verify
```bash
npx tsc --noEmit
npm run build
npm test
```
Also open a real chat route and verify an ordinary message, a tool call, an approval gate if present, a thread switch, and the project’s persisted-history path. Run npx assistant-ui@latest doctor and npx assistant-ui@latest info when a dependency or environment mismatch remains.
## Common Gotchas
**The upgrade command changed imports but the app still uses the old adapter**
- The package update only covers @assistant-ui packages. Update ai and @ai-sdk/react separately, then follow the AI SDK reference.
- @assistant-ui/react-ai-sdk is an alias for older installs. Current source imports from @assistant-ui/ai-sdk.
**AuiProvider or AssistantRuntimeProvider no longer accepts the old props**
- useAui() is context access only. Build AuiConfig({...}) and pass it as config.
- A nested AuiProvider needs extends={aui}; an isolated one needs extends={null}.
**A scope lookup no longer behaves like a null check**
- aui.thread is always truthy. Check aui.thread.source != null before accessing an optional scope.
- Scope accessors are properties. Call scope methods, not the scope itself.
**The typecheck still finds removed hooks or tool maps**
- Apply the full removed-hook mapping in [assistant-ui.md](./references/assistant-ui.md).
- Replace s.tools.tools with s.tools.toolUIs and the mcp-app group key with standalone-tool-call.
## Related Skills
- [setup](../setup/SKILL.md) -- install assistant-ui into a project that has not used it before
- [runtime](../runtime/SKILL.md) -- build or customize an active runtime after the migration
- [tools](../tools/SKILL.md) -- author toolkits, frontend tools, approvals, and tool UI
- [elements](../elements/SKILL.md) -- install and customize the copied Elements registry components