
Update
- 3.7k installs
- 20 repo stars
- Updated August 2, 2026
- assistant-ui/skills
update is a skill that upgrades existing assistant-ui projects through AI SDK and assistant-ui version migrations so developers can resolve peer conflicts and renamed APIs.
About
The update skill upgrades existing assistant-ui projects to current releases and runs the resulting migrations. Use it when bumping @assistant-ui/react, @assistant-ui/react-ai-sdk, ai, or @ai-sdk/react, resolving peer-dependency conflicts, or fixing post-upgrade type errors from renamed APIs. Phase one detects installed versus latest versions via npm ls and npm view, then routes through breaking-change references for each jump. AI SDK below 6.0.0 triggers the v4 or v5 to v6 migration guide covering toDataStreamResponse to toUIMessageStreamResponse and maxSteps to stopWhen stepCountIs. assistant-ui below 0.14.0 covers primitives components prop to children render functions, ViewportSlack removal, and useAssistantApi to useAui. Migration order is AI SDK first, then assistant-ui, then verification with npx tsc --noEmit and pnpm build. Execution runs npx assistant-ui@latest upgrade and pnpm or npm add @latest. Troubleshooting covers peer conflicts, type errors after upgrade, and runtime API mismatches. For first-time install or fresh scaffold, the setup skill is the correct entry point instead of update.
- Detects installed versus latest @assistant-ui/react, react-ai-sdk, ai, and @ai-sdk/react versions.
- Routes AI SDK below 6.0.0 and assistant-ui below 0.14.0 through version-specific migration references.
- Migration order: AI SDK first, assistant-ui second, then npx tsc --noEmit and build verification.
- Covers renamed APIs including useAui, runtime.threads, and toUIMessageStreamResponse.
- Explicitly defers first-time installs to the setup skill rather than update.
Update by the numbers
- 3,691 all-time installs (skills.sh)
- +216 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #138 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
update capabilities & compatibility
- Capabilities
- version detection via npm ls and npm view · ai sdk v6 migration routing · assistant ui breaking change migrations · type check and build verification after upgrade
- Use cases
- frontend · refactoring
What update says it does
Upgrades an existing assistant-ui project to current releases and executes the resulting migrations.
For a first-time install or fresh scaffold (not an upgrade) use setup instead.
npx skills add https://github.com/assistant-ui/skills --skill updateAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3.7k |
|---|---|
| repo stars | ★ 20 |
| Security audit | 2 / 3 scanners passed |
| Last updated | August 2, 2026 |
| Repository | assistant-ui/skills ↗ |
How do I upgrade assistant-ui and AI SDK packages without breaking peer dependencies or post-upgrade type errors?
Upgrade an existing assistant-ui project through AI SDK and assistant-ui version migrations with package bumps and type verification.
Who is it for?
Teams maintaining an existing assistant-ui chat UI who need guided version jumps and breaking-change fixes.
Skip if: Skip for first-time assistant-ui install or fresh scaffold; use the setup skill instead.
When should I use this skill?
User wants to update, upgrade, bump, or migrate @assistant-ui/react, ai, or @ai-sdk/react after a version jump.
What you get
Updated package versions with migrations applied and type-check or build verification passing.
- AI SDK v6 migrated codebase
- Verified build and test results
By the numbers
- Defines 6 migration phases from research through final verification
- Covers Vercel AI SDK v4.x and v5.x upgrades to v6.x
Files
assistant-ui Update
Always verifies against npm ground truth and GitHub commits.
References
- ./references/ai-sdk-v6.md -- AI SDK v4/v5 → v6 migration (complete guide)
- ./references/assistant-ui.md -- assistant-ui version migrations
- ./references/breaking-changes.md -- Quick reference table
Phase 1: Detect Versions
Get Ground Truth
npm ls @assistant-ui/react @assistant-ui/react-ai-sdk ai @ai-sdk/react 2>/dev/null
npm view @assistant-ui/react version
npm view @assistant-ui/react-ai-sdk version
npm view ai versionVersion Analysis
Current latest: @assistant-ui/react 0.14.x, @assistant-ui/react-ai-sdk 1.3.x, assistant-stream 0.3.x.
| Package | Check For |
|---|---|
ai | < 6.0.0 → needs AI SDK v6 migration |
@assistant-ui/react | < 0.14.0 → primitives components prop replaced by children render functions; deprecated hooks/aliases removed |
@assistant-ui/react | < 0.13.0 → ThreadPrimitive.ViewportSlack removed (top-anchor changes) |
@assistant-ui/react | < 0.12.0 → unified state API (useAui/useAuiState/useAuiEvent/AuiIf) |
@assistant-ui/react | < 0.11.0 → runtime rearchitecture |
@assistant-ui/react | < 0.10.0 → ESM only |
@assistant-ui/react | < 0.8.0 → UI split (shadcn registry) |
@assistant-ui/react-ai-sdk | < 1.0.0 → needs AI SDK v6 first |
Phase 2: Route to Migration
AI SDK < 6.0.0?
├─ Yes → See ./references/ai-sdk-v6.md
└─ No
└─ assistant-ui outdated?
├─ Yes → See ./references/assistant-ui.md
└─ No → Already up to dateMigration Order
1. AI SDK first (if < 6.0.0) - Required for @assistant-ui/react-ai-sdk >= 1.0 2. assistant-ui second - Apply breaking changes for version jump 3. Verify - Type check, build, test
Phase 3: Execute
Update Packages
pnpm add @assistant-ui/react@latest @assistant-ui/react-ai-sdk@latest ai@latest @ai-sdk/react@latest
npm install @assistant-ui/react@latest @assistant-ui/react-ai-sdk@latest ai@latest @ai-sdk/react@latestApply Migrations
Based on version jump, apply relevant migrations from references.
Verify
npx tsc --noEmit
pnpm buildTroubleshooting
"Peer dependency conflict"
- Update all packages together
- Check version compatibility in ./references/breaking-changes.md
Type errors after upgrade
- Consult breaking changes reference
- Check specific migration guide
Runtime errors
- Verify API patterns match new version
- Check for renamed/moved APIs
AI SDK v6 Migration
Migrate a codebase from AI SDK v4 or v5 to v6. This is a methodical, careful process using agents. Do not rush. Verify everything.
Covers: v4.x → v6.x, v5.x → v6.x
Official docs: https://ai-sdk.dev/docs/migration-guides/migration-guide-6-0
---
Contents
- Critical Rules
- Phase 1: Deep Research (Use Agents)
- Phase 2: Create Detailed Migration Plan
- Phase 3: Execute Migration
- Phase 4: Build Verification
- Phase 5: Test Verification
- Phase 6: Final Verification
- Rollback Plan
- Package Updates
- Automated Migration
- Core Breaking Changes
- UI & React Changes
- Tool System Changes
- Streaming Architecture
- Structured Output Changes
- Provider-Specific Changes
- assistant-ui Specific Changes
- Complete Migration Examples
- Environment Configuration
- Test Utilities
- New Utilities in v6
- v4-Specific Changes (v4 → v6 Direct Migration)
- Migration Checklist
Critical Rules
1. NEVER make changes without reading files first - Always read the full file before editing 2. NEVER guess - If unsure, search the codebase or ask the user 3. ALWAYS verify after changes - Run type check after each file modification 4. USE AGENTS for research - Spawn Explore agents for thorough codebase analysis 5. TRACK EVERYTHING - Use TodoWrite to track every file and change 6. ONE CHANGE AT A TIME - Make atomic changes, verify, then proceed 7. REFERENCE THE GUIDE - The complete migration guide is embedded below - consult it for every change
---
Phase 1: Deep Research (Use Agents)
STOP. Do not skip this phase. Thorough research prevents mistakes.
1.1 Spawn Research Agent for AI SDK Patterns
Use Task tool with subagent_type: "Explore" and model: "opus":
Thoroughly search this codebase for ALL AI SDK usage. Find EVERY instance of:
IMPORTS TO FIND:
- import from "ai"
- import from "@ai-sdk/*"
- import from "@assistant-ui/react-ai-sdk"
PATTERNS TO FIND:
- useChat hook usage
- streamText / generateText calls
- generateObject / streamObject calls
- convertToCoreMessages calls
- CoreMessage / Message types
- maxSteps configuration
- tool definitions (look for parameters:, execute:)
- addToolResult calls
- textEmbedding / textEmbeddingModel
- Experimental_Agent
- toDataStreamResponse
For EACH finding, report:
- Exact file path
- Line numbers
- The actual code snippet
- What v6 change applies to it
Be exhaustive. Missing something causes migration failures.1.2 Spawn Research Agent for Package Analysis
Use Task tool with subagent_type: "Explore":
Find and analyze all package.json files in this repository.
For each package.json, extract:
1. Current versions of: ai, @ai-sdk/*, zod, @assistant-ui/*
2. The package manager (look for pnpm-lock.yaml, yarn.lock, package-lock.json)
3. Test scripts (test, test:watch, etc.)
4. Build scripts
Report the exact current versions vs required v6 versions.1.3 Spawn Research Agent for Test Infrastructure
Use Task tool with subagent_type: "Explore":
Find the test infrastructure in this codebase:
1. Test framework (vitest, jest, etc.)
2. Test file locations and patterns
3. Any AI SDK test mocks (MockLanguageModelV2, etc.)
4. The exact command to run tests
5. Any test configuration files1.4 Compile Research Results
After ALL agents complete, create a comprehensive findings document:
- Total files requiring changes
- Categorized list of all patterns found
- Package versions needing update
- Test command to use
- Any unusual patterns or edge cases
CHECKPOINT: Present findings to user. Ask if anything was missed. Do not proceed until confirmed.
---
Phase 2: Create Detailed Migration Plan
Based on Phase 1 findings and the migration guide below, create a file-by-file plan.
2.1 Categorize Changes
Group findings into categories:
Category A: Codemod-handled (automatic)
- CoreMessage → ModelMessage
- convertToCoreMessages → convertToModelMessages
- textEmbedding/textEmbeddingModel → embedding/embeddingModel (on providers)
- ToolCallOptions → ToolExecutionOptions
Category B: Manual - Simple renames
- Message → UIMessage
- maxSteps → stopWhen: stepCountIs(n)
Category C: Manual - Structural changes
- Adding await to convertToModelMessages
- toDataStreamResponse → toUIMessageStreamResponse
- generateObject → generateText + Output.object
- Tool definition restructuring
- useChat hook changes
Category D: Manual - Complex logic
- Message parts array handling
- Custom stream implementations
- Tool result handling changes
2.2 Create File-by-File Plan
For EACH file that needs changes, document:
FILE: path/to/file.ts
CHANGES NEEDED:
1. Line X: [old] → [new] (Category: X)
2. Line Y: [old] → [new] (Category: X)
IMPORTS TO ADD: [list]
IMPORTS TO REMOVE: [list]
VERIFICATION: What to check after editing2.3 Determine Execution Order
Order matters. Follow this sequence:
1. Package updates (package.json) 2. Run codemods 3. Type definition files 4. Utility/helper files 5. API routes 6. React components 7. Test files
CHECKPOINT: Present full plan to user for approval. Do not proceed without explicit approval.
---
Phase 3: Execute Migration
Only proceed after user approves the plan.
3.1 Update Packages
Detect package manager and run appropriate command:
# For pnpm
pnpm add ai@latest @ai-sdk/react@latest @ai-sdk/openai@latest zod@latest @assistant-ui/react@latest @assistant-ui/react-ai-sdk@latest
# For npm
npm install ai@latest @ai-sdk/react@latest @ai-sdk/openai@latest zod@latest
# For yarn
yarn add ai@latest @ai-sdk/react@latest @ai-sdk/openai@latest zod@latestVERIFY: Check package.json shows correct versions before continuing.
3.2 Run Codemods
npx @ai-sdk/codemod upgradeThis is the recommended approach - it detects your current version and applies all necessary codemods (v4→v5→v6) automatically.
VERIFY:
- Review codemod output
- Run
git diffto see what changed - Check for any errors or warnings
3.3 Apply Manual Changes
For EACH file in the plan:
1. Add to todo list as "in_progress" 2. Read the ENTIRE file first 3. Make changes ONE AT A TIME 4. After each change, verify syntax is valid 5. After all changes to file, run type check 6. Mark as "completed" only after type check passes
IMPORTANT PATTERNS FROM GUIDE:
API Route changes:
// OLD
const result = streamText({ model, messages, maxSteps: 10 });
return (await result).toDataStreamResponse();
// NEW
import { stepCountIs } from "ai";
const result = streamText({
model,
messages: await convertToModelMessages(messages),
stopWhen: stepCountIs(10)
});
return result.toUIMessageStreamResponse();Tool definitions:
// OLD
tools: {
myTool: {
description: "...",
parameters: z.object({ ... }),
execute: async (args) => { ... }
}
}
// NEW
import { tool, zodSchema } from "ai";
tools: {
myTool: tool({
description: "...",
inputSchema: zodSchema(z.object({ ... })),
execute: async (args, options) => { ... }
})
}3.4 Type Check After Each File
npx tsc --noEmit
# or
pnpm type-check
# or whatever the project usesIf errors found: 1. Read the error carefully 2. Consult the migration guide below 3. Fix the specific error 4. Re-run type check 5. Repeat until clean
---
Phase 4: Build Verification
4.1 Full Type Check
Run full TypeScript compilation:
npx tsc --noEmit4.2 Fix All Type Errors
For each error: 1. Add to todo list 2. Read the file and surrounding context 3. Identify which v6 change applies 4. Apply the fix from the guide 5. Verify the fix 6. Mark complete
Common type errors and fixes:
Property 'content' does not exist on type 'UIMessage'→ Usemessage.partsarrayType 'CoreMessage' not found→ Change toModelMessagemaxSteps does not exist→ UsestopWhen: stepCountIs(n)toDataStreamResponse not found→ UsetoUIMessageStreamResponse()
4.3 Build Check
pnpm build
# or
npm run buildFix any build errors before proceeding.
---
Phase 5: Test Verification
5.1 Run Test Suite
pnpm test
# or
npm test5.2 Fix Failing Tests
For EACH failing test:
1. Read the test file 2. Read the error message carefully 3. Determine if it's a:
- Test mock issue (V2 → V3)
- Assertion issue (message structure changed)
- Implementation issue (missed migration step)
4. Apply appropriate fix 5. Re-run that specific test 6. Verify it passes 7. Move to next failing test
5.3 Full Test Pass
Run complete test suite again. All tests must pass.
---
Phase 6: Final Verification
6.1 Manual Testing Checklist
Ask user to verify:
- [ ] Dev server starts without errors
- [ ] Chat messages send successfully
- [ ] Streaming responses work
- [ ] Tool calls execute correctly
- [ ] Tool results display properly
- [ ] No console errors
6.2 Cleanup
- Remove any TODO comments added during migration
- Remove unused imports
- Run linter/formatter
---
Rollback Plan
If migration fails catastrophically:
git checkout .
git clean -fdThen re-analyze what went wrong before retrying.
---
COMPLETE MIGRATION GUIDE REFERENCE
Consult this for EVERY change. Do not guess.
Package Updates
Required Package Versions
{
"ai": "^6.0.0",
"@ai-sdk/react": "^3.0.0",
"@ai-sdk/provider": "^3.0.0",
"@ai-sdk/provider-utils": "^4.0.0",
"@assistant-ui/react": "^0.14.13",
"@assistant-ui/react-ai-sdk": "^1.3.31"
}Provider Packages
All @ai-sdk/* provider packages should be updated to ^3.0.0:
{
"@ai-sdk/openai": "^3.0.0",
"@ai-sdk/anthropic": "^3.0.0",
"@ai-sdk/google": "^3.0.0",
"@ai-sdk/mistral": "^3.0.0"
}MCP Package (if using MCP)
MCP has been moved to a separate package:
{
"@ai-sdk/mcp": "^1.0.0"
}Zod Support
AI SDK v6 supports both Zod 3.25+ and Zod 4.x:
{
"zod": "^3.25.76 || ^4.1.8"
}---
Automated Migration
The AI SDK provides codemods to automate many migration tasks:
# From v4: Run ALL codemods (v4 → v5 → v6)
npx @ai-sdk/codemod upgrade
# From v5: Run v6 codemods only (v5 → v6)
npx @ai-sdk/codemod v6
# Run specific codemods
npx @ai-sdk/codemod v6/rename-core-message-to-model-message src/
npx @ai-sdk/codemod v6/add-await-converttomodelmessages src/
npx @ai-sdk/codemod v5/move-maxsteps-to-stopwhen src/Which command to use:
upgrade- Recommended for v4 projects. Runs all v4, v5, and v6 codemods.v6- For v5 projects. Runs only v6 codemods.v5- For v4 projects wanting incremental migration. Runs only v5 codemods.v4- For v3 projects. Runs only v4 codemods.
Available v6 Codemods (v5 → v6)
| Codemod | Description |
|---|---|
v6/add-await-converttomodelmessages | Adds await to convertToModelMessages() calls |
v6/rename-converttocoremessages-to-converttomodelmessages | Updates the conversion function name |
v6/rename-core-message-to-model-message | Renames CoreMessage → ModelMessage |
v6/rename-mock-v2-to-v3 | Updates test mock classes from V2 to V3 |
v6/rename-text-embedding-to-embedding | Renames textEmbeddingModel → embeddingModel on providers |
v6/rename-tool-call-options-to-tool-execution-options | Renames ToolCallOptions → ToolExecutionOptions |
v6/rename-vertex-provider-metadata-key | Updates google → vertex for metadata keys |
Key v5 Codemods (v4 → v5, needed for v4 → v6)
| Codemod | Description |
|---|---|
v5/move-maxsteps-to-stopwhen | Moves maxSteps to stopWhen: stepCountIs(n) |
v5/rename-max-tokens-to-max-output-tokens | Renames maxTokens → maxOutputTokens |
v5/rename-tool-parameters-to-inputschema | Renames tool parameters → inputSchema |
v5/replace-usechat-api-with-transport | Replaces useChat({ api }) with transport |
v5/replace-usechat-input-with-state | Removes managed input state from useChat |
v5/replace-content-with-parts | Replaces message.content with message.parts |
v5/rename-message-to-ui-message | Renames Message → UIMessage |
v5/rename-datastream-methods-to-uimessage | Renames stream methods to UI message variants |
Note: Review all automated changes manually, especially around async/await additions.
---
Core Breaking Changes
1. Message Type Changes
CoreMessage has been replaced with ModelMessage:
- import { CoreMessage, convertToCoreMessages } from "ai";
+ import { ModelMessage, convertToModelMessages } from "ai";Message has been replaced with UIMessage:
- import type { Message } from "ai";
+ import type { UIMessage } from "ai";2. convertToModelMessages is Now Async
This is a critical change that affects all API routes:
// Before (v5)
- const modelMessages = convertToCoreMessages(messages);
// After (v6) - MUST use await
+ const modelMessages = await convertToModelMessages(messages);3. maxSteps Replaced with stopWhen
+ import { stepCountIs } from "ai";
const result = streamText({
model: openai("gpt-4o"),
messages: modelMessages,
- maxSteps: 10,
+ stopWhen: stepCountIs(10),
});4. Agent Class Changes
Experimental_Agent has been replaced with ToolLoopAgent:
- import { Experimental_Agent } from "ai";
+ import { ToolLoopAgent } from "ai";
const agent = new ToolLoopAgent({
- system: "You are a helpful assistant",
+ instructions: "You are a helpful assistant",
// Note: Default stopWhen changed from stepCountIs(1) to stepCountIs(20)
});4.1 Agent Stream Response Renamed
- import { createAgentStreamResponse } from "ai";
+ import { createAgentUIStreamResponse } from "ai";
- return createAgentStreamResponse({ ... });
+ return createAgentUIStreamResponse({ ... });The messages property in the result has been renamed to uiMessages:
- const { messages } = await createAgentUIStreamResponse({ ... });
+ const { uiMessages } = await createAgentUIStreamResponse({ ... });5. Tool Call Options Renamed
- import type { ToolCallOptions } from "ai";
+ import type { ToolExecutionOptions } from "ai";6. Embedding Method Renames
Provider embedding methods were renamed:
- const model = openai.textEmbedding("text-embedding-3-small");
+ const model = openai.embedding("text-embedding-3-small");
// Alternative (also renamed):
- const model = openai.textEmbeddingModel("text-embedding-3-small");
+ const model = openai.embeddingModel("text-embedding-3-small");Note: The core embed() and embedMany() functions from the "ai" package remain unchanged. Only the provider methods were renamed.
7. MCP Imports Moved to Separate Package
If you're using MCP (Model Context Protocol), imports have moved from ai to @ai-sdk/mcp:
- import { experimental_createMCPClient } from "ai";
- import { Experimental_StdioMCPTransport } from "ai/mcp-stdio";
+ import { experimental_createMCPClient } from "@ai-sdk/mcp";
+ import { Experimental_StdioMCPTransport } from "@ai-sdk/mcp/mcp-stdio";Note: Install the new package: pnpm add @ai-sdk/mcp
8. Warning Type Unification
Separate warning types consolidated into a single Warning type:
- import type { GenerateTextWarning, StreamTextWarning, CallWarning } from "ai";
+ import type { Warning } from "ai";9. Finish Reason Change
The "unknown" finish reason now returns as "other":
- if (result.finishReason === "unknown") { }
+ if (result.finishReason === "other") { }10. Tool UI Helper Renames
The naming changed to distinguish static vs dynamic tools:
// For static tools only:
- import { isToolUIPart, getToolName } from "ai";
+ import { isStaticToolUIPart, getStaticToolName } from "ai";
// For both static and dynamic tools (the new default):
- import { isToolOrDynamicToolUIPart, getToolOrDynamicToolName } from "ai";
+ import { isToolUIPart, getToolName } from "ai";11. Tool.toModelOutput Signature Change
const myTool = tool({
// ...
// Before
- toModelOutput: (output) => processOutput(output),
// After - requires object destructuring
+ toModelOutput: ({ output }) => processOutput(output),
});12. ToolCallRepairFunction Change
The system parameter now accepts different types:
// system parameter type changed
- system: string | undefined
+ system: string | SystemModelMessage | undefined
// Handle both types:
const repair: ToolCallRepairFunction = async ({ system }) => {
const systemText = typeof system === 'string' ? system : system?.content;
};13. Token Usage Property Changes
// Cached input tokens
- result.usage.cachedInputTokens
+ result.usage.inputTokenDetails.cacheReadTokens
// Reasoning tokens
- result.usage.reasoningTokens
+ result.usage.outputTokenDetails.reasoningTokens14. Rerank Score Property Renamed
// For reranking results
- result.relevanceScore
+ result.score---
UI & React Changes
1. UIMessage Structure
The fundamental message format changed from a single content string to a parts array:
// Old structure (v5)
interface Message {
id: string;
role: "user" | "assistant";
content: string;
}
// New structure (v6)
interface UIMessage {
id: string;
role: "user" | "assistant" | "system";
parts: MessagePart[];
metadata?: Record<string, unknown>;
}2. Message Part Types
The parts array supports multiple content types:
type MessagePart =
| { type: "text"; text: string }
| { type: "file"; file: FileInfo }
| { type: "reasoning"; text: string }
| { type: "tool-invocation"; toolInvocation: ToolInvocation }
| { type: "source-url"; sourceId: string; url: string; title?: string }
| { type: "source-document"; sourceId: string; ... }
| { type: `data-${string}`; data: unknown }; // Custom data parts3. Reading Text from Messages
const extractText = (messages: UIMessage[]): string => {
return messages
.map((m) =>
m.parts
.filter((p): p is { type: "text"; text: string } => p.type === "text")
.map((p) => p.text)
.join(" ")
)
.join("\n");
};4. useChat Hook Changes
The useChat hook underwent significant restructuring in v6.
Input State Management
Input is no longer managed internally by the hook:
// Before (v5)
- const { input, setInput, handleSubmit } = useChat();
- <input value={input} onChange={(e) => setInput(e.target.value)} />
// After (v6) - manage input state yourself
+ const [input, setInput] = useState("");
+ const { sendMessage } = useChat();
+
+ const handleSubmit = () => {
+ sendMessage(input);
+ setInput("");
+ };Message Sending
append() replaced with sendMessage():
// Before (v5)
- append({ role: "user", content: "Hello" });
// After (v6) - multiple valid formats:
// Option 1: Simple string
+ sendMessage("Hello");
// Option 2: Object with text
+ sendMessage({ text: "Hello" });
// Option 3: Object with parts array
+ sendMessage({
+ parts: [{ type: "text", text: "Hello" }]
+ });
// With options (headers, body, metadata)
+ sendMessage("Hello", { metadata: { key: "value" } });Tool Result Handling
AI SDK v6 provides two methods for submitting tool results:
// addToolResult - Simple form (without explicit state)
addToolResult({
tool: "toolName",
toolCallId,
output: result,
});
// addToolOutput - With explicit state (for success or error)
addToolOutput({
state: "output-available",
tool: "toolName",
toolCallId,
output: result,
});
// For errors, use addToolOutput with error state:
addToolOutput({
state: "output-error",
tool: "toolName",
toolCallId,
errorText: "Error message",
});Note: Both addToolResult and addToolOutput are available. Use addToolOutput when you need to explicitly set the state (especially for errors).
5. Status States
The hook returns a status field with four possible values:
type ChatStatus = "submitted" | "streaming" | "ready" | "error";
const { status } = useChat();
// submitted: Message sent, awaiting response stream start
// streaming: Response actively receiving data chunks
// ready: Response complete, ready for new messages
// error: Request failed6. Transport Configuration
The transport-based architecture replaces the old api option:
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
// Before (v5)
const { messages } = useChat({
api: "/api/chat",
});
// After (v6)
const { messages } = useChat({
transport: new DefaultChatTransport({
api: "/api/chat",
headers: { /* ... */ },
body: { /* ... */ },
credentials: "include",
}),
});7. Message Validation for Persistence
When loading messages from storage that contain tools or custom data, validate them:
import { validateUIMessages } from "ai";
// Before using stored messages
const validatedMessages = await validateUIMessages({
messages: storedMessages,
tools: yourTools,
});---
Tool System Changes
Important: AI SDK and assistant-ui use different property names for tool schemas:
- AI SDK `tool()` helper (backend): uses
inputSchema - assistant-ui `useAssistantTool` (frontend): uses
parameters
This distinction matters when defining tools in different contexts.
1. Tool Definition with tool() Helper
The tool() helper provides type inference between schema and execute function:
import { tool } from "ai";
import { z } from "zod";
const weatherTool = tool({
description: "Get weather for a location",
// inputSchema accepts Zod schemas directly
inputSchema: z.object({
location: z.string().describe("The location to get weather for"),
unit: z.enum(["celsius", "fahrenheit"]).optional(),
}),
execute: async ({ location, unit }, options) => {
// options includes: toolCallId, messages, abortSignal
return { temperature: 72, unit: unit ?? "fahrenheit" };
},
// Optional: Enable strict mode for providers that support it
strict: true,
});2. Schema Options
You can use Zod schemas directly (auto-converted) or wrap them with helpers:
import { tool, zodSchema, jsonSchema } from "ai";
import { z } from "zod";
// Option 1: Direct Zod (auto-converted to JSON Schema)
const tool1 = tool({
inputSchema: z.object({ query: z.string() }),
execute: async ({ query }) => { /* ... */ },
});
// Option 2: zodSchema() wrapper (explicit, recommended for clarity)
// This is what the assistant-ui examples use
const tool2 = tool({
inputSchema: zodSchema(
z.object({ query: z.string() }),
),
execute: async ({ query }) => { /* ... */ },
});
// Option 3: zodSchema() with options (for recursive schemas)
const tool3 = tool({
inputSchema: zodSchema(
z.object({ category: categorySchema }),
{ useReferences: true } // Enables recursive schema support
),
execute: async ({ category }) => { /* ... */ },
});
// Option 4: jsonSchema() for JSON Schema objects
const tool4 = tool({
inputSchema: jsonSchema<{ query: string }>({
type: "object",
properties: { query: { type: "string" } },
required: ["query"],
}),
execute: async ({ query }) => { /* ... */ },
});Note: When using .describe() or .meta() on Zod schemas, these methods must be called last in the chain, as Zod returns new instances for most operations.
3. Per-Tool Strict Mode
Strict JSON schema validation moved from provider options to individual tools:
const result = streamText({
model: openai("gpt-4o"),
- providerOptions: {
- openai: { strictJsonSchema: true },
- },
tools: {
myTool: tool({
inputSchema: schema,
+ strict: true, // Per-tool strict mode
execute: async (input) => { /* ... */ },
}),
},
});4. Tool States
Tool invocations now have explicit states:
type ToolInvocationState =
| "input-streaming" // Arguments being streamed
| "input-available" // Arguments complete, not yet executed
| "output-available" // Execution complete with result
| "output-error"; // Execution failed
message.parts.forEach(part => {
if (isToolUIPart(part)) {
console.log(part.state); // One of the above states
console.log(part.toolCallId); // Unique ID
console.log(part.input); // Tool arguments
console.log(part.output); // Result (if output-available)
console.log(part.errorText); // Error (if output-error)
}
});5. Tool Input Lifecycle Hooks
Tools now support streaming callbacks:
const myTool = tool({
inputSchema: z.object({ query: z.string() }),
execute: async ({ query }) => { /* ... */ },
// Called when model starts generating arguments
onInputStart: ({ toolCallId }) => {
console.log("Tool input started:", toolCallId);
},
// Called for each input chunk (streamText only)
onInputDelta: ({ toolCallId, delta }) => {
console.log("Input delta:", delta);
},
// Called when complete, validated input is available
onInputAvailable: ({ toolCallId, input }) => {
console.log("Input ready:", input);
},
});6. Tool Execution Approval
Tools can require user confirmation:
// Server: Mark tool as needing approval
const dangerousTool = tool({
description: "Deletes a file",
inputSchema: z.object({ path: z.string() }),
needsApproval: true, // Requires client approval
execute: async ({ path }) => { /* ... */ },
});
// Client: Handle approval
const { addToolApprovalResponse } = useChat();
addToolApprovalResponse({ toolCallId, approved: true });
addToolApprovalResponse({ toolCallId, approved: false });7. Frontend Tools Helper (assistant-ui)
When forwarding tools defined in the frontend to your backend:
import { frontendTools } from "@assistant-ui/react-ai-sdk";
export async function POST(req: Request) {
const { messages, tools } = await req.json();
const result = streamText({
model: openai("gpt-4o"),
messages: await convertToModelMessages(messages),
tools: {
...frontendTools(tools),
myBackendTool: tool({ /* ... */ }),
},
});
return result.toUIMessageStreamResponse();
}---
Streaming Architecture
1. Stream Response Methods
// Before (v5) - result was a promise
- return (await result).toDataStreamResponse();
// After (v6) - result is not a promise
+ return result.toDataStreamResponse();
// For UI message streams (recommended for assistant-ui):
+ return result.toUIMessageStreamResponse();2. toUIMessageStreamResponse Options
return result.toUIMessageStreamResponse({
// Include reasoning tokens (for models that support it)
sendReasoning: true,
// Include source citations (for RAG models)
sendSources: true,
// Custom ID generator for messages
generateMessageId: () => crypto.randomUUID(),
// Attach metadata to message parts
messageMetadata: { timestamp: Date.now() },
// Customize error messages sent to client
getErrorMessage: (error) => `Error: ${error.message}`,
// Handle completion (good for persistence)
onFinish: ({ messages, responseMessage }) => {
// Save to database
},
});3. Stream Protocol Changes
The protocol evolved to lifecycle events with three-phase patterns:
// Text streaming uses start/delta/end with unique IDs
{ type: "text-start", id: "text-1" }
{ type: "text-delta", id: "text-1", delta: "Hello" }
{ type: "text-delta", id: "text-1", delta: " world" }
{ type: "text-end", id: "text-1" }
// Tool inputs stream progressively
{ type: "tool-input-start", toolCallId: "call-1" }
{ type: "tool-input-delta", toolCallId: "call-1", delta: '{"loc' }
{ type: "tool-input-end", toolCallId: "call-1" }4. Custom Stream Headers
When providing streams from a custom backend, set the required header:
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"x-vercel-ai-ui-message-stream": "v1",
},
});5. Creating Custom UI Message Streams
import { createUIMessageStream, createUIMessageStreamResponse } from "ai";
const stream = createUIMessageStream({
execute: async ({ writer }) => {
// Write text manually
writer.write({ type: "text-start", id: "text-1" });
writer.write({ type: "text-delta", id: "text-1", delta: "Hello" });
writer.write({ type: "text-end", id: "text-1" });
// Write custom data (persistent - saved in message.parts)
writer.write({
type: "data-weather",
id: "weather-1",
data: { city: "NYC", temp: 72 },
});
// Merge another stream
const result = streamText({ model, messages });
writer.merge(result.toUIMessageStream());
},
onFinish: ({ messages, responseMessage }) => {
// Handle completion
},
});
return createUIMessageStreamResponse({ stream });6. Custom Data Parts
Defining Type-Safe Data Parts
// Define your message type with data parts
export type MyUIMessage = UIMessage<
never,
{
weather: { city: string; temp: number; status: "loading" | "ready" };
notification: { message: string; level: "info" | "warning" | "error" };
}
>;Server: Sending Data Parts
const stream = createUIMessageStream<MyUIMessage>({
execute: ({ writer }) => {
// Persistent data part (appears in message.parts)
writer.write({
type: "data-weather",
id: "weather-1",
data: { city: "NYC", temp: 72, status: "ready" },
});
// Update same part by using same ID
writer.write({
type: "data-weather",
id: "weather-1",
data: { city: "NYC", temp: 75, status: "ready" },
});
},
});Client: Reading Data Parts
// Persistent parts in message.parts
const weatherData = message.parts
.filter((part) => part.type === "data-weather")
.map((part) => part.data);
// Transient parts via onData callback (not saved in message history)
const { messages } = useChat<MyUIMessage>({
onData: (dataPart) => {
if (dataPart.type === "data-notification") {
showToast(dataPart.data.message);
}
},
});---
Structured Output Changes
generateObject and streamObject Deprecated
Use generateText and streamText with the Output helper instead:
// Before (v5)
- import { generateObject } from "ai";
- const { object } = await generateObject({
- model: openai("gpt-4o"),
- schema: z.object({ name: z.string() }),
- prompt: "Generate a name",
- });
// After (v6)
+ import { generateText, Output } from "ai";
+ const { output } = await generateText({
+ model: openai("gpt-4o"),
+ output: Output.object({
+ schema: z.object({ name: z.string() }),
+ }),
+ prompt: "Generate a name",
+ });Output Types
import { generateText, streamText, Output } from "ai";
// Single object
const { output } = await generateText({
model,
output: Output.object({
schema: z.object({ name: z.string(), age: z.number() }),
name: "person", // Optional: helps model understand context
description: "...", // Optional: additional guidance
}),
prompt: "Generate a person",
});
// Array of objects
const { output } = await generateText({
model,
output: Output.array({
schema: z.object({ name: z.string() }),
}),
prompt: "Generate 5 names",
});
// Choice from options
const { output } = await generateText({
model,
output: Output.choice({
options: ["positive", "negative", "neutral"],
}),
prompt: "Classify the sentiment",
});
// Plain JSON (no validation)
const { output } = await generateText({
model,
output: Output.json(),
prompt: "Generate JSON data",
});Streaming Structured Output
// Before (v5)
- const { partialObjectStream } = streamObject({ ... });
- for await (const partial of partialObjectStream) { }
// After (v6)
+ const result = streamText({
+ model,
+ output: Output.object({ schema }),
+ prompt: "...",
+ });
+ for await (const partial of result.partialOutputStream) { }For arrays, use elementStream to get complete elements:
const result = streamText({
model,
output: Output.array({ schema }),
prompt: "Generate items",
});
// Each element is complete and validated
for await (const element of result.elementStream) {
console.log(element); // Fully validated element
}---
Provider-Specific Changes
OpenAI
strictJsonSchemanow defaults totrue(wasfalse)- Disable if needed:
const result = await generateText({
model: openai("gpt-4o"),
providerOptions: {
openai: { strictJsonSchema: false },
},
// ...
});Azure OpenAI
- Default behavior switches to Responses API
- Use
azure.chat()for previous Chat Completions API behavior - Metadata key changed:
openai→azure
// For Responses API (new default)
const model = azure("gpt-4o");
// For Chat Completions API (previous behavior)
const model = azure.chat("gpt-4o");
// Metadata access
- result.experimental_providerMetadata?.openai
+ result.experimental_providerMetadata?.azure
// Provider options
- providerOptions: { openai: { ... } }
+ providerOptions: { azure: { ... } }Anthropic
New structuredOutputMode option for Claude Sonnet 4.5+:
const result = await generateText({
model: anthropic("claude-sonnet-4-5-20250929"),
output: Output.object({ schema }),
providerOptions: {
anthropic: {
// Options: 'outputFormat', 'jsonTool', or 'auto' (default)
structuredOutputMode: "outputFormat",
},
},
});Google Vertex
Metadata and options key changed:
- providerOptions: { google: { safetySettings: [...] } }
+ providerOptions: { vertex: { safetySettings: [...] } }
- result.experimental_providerMetadata?.google
+ result.experimental_providerMetadata?.vertex---
assistant-ui Specific Changes
1. Simplified Client Setup
The simplest case works with zero configuration:
"use client";
import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
export function Chat() {
// Defaults to AssistantChatTransport with /api/chat endpoint
const runtime = useChatRuntime();
return (
<AssistantRuntimeProvider runtime={runtime}>
{/* Your chat UI */}
</AssistantRuntimeProvider>
);
}2. Custom Endpoint Configuration
For custom API endpoints:
import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/react-ai-sdk";
// Option 1: AssistantChatTransport (recommended)
// Automatically forwards system messages and tools from context
const runtime = useChatRuntime({
transport: new AssistantChatTransport({
api: "/my-custom-api/chat",
}),
});For standard AI SDK transport without automatic forwarding:
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
import { DefaultChatTransport } from "ai";
// Option 2: DefaultChatTransport (from "ai" package)
// Does NOT auto-forward system/tools
const runtime = useChatRuntime({
transport: new DefaultChatTransport({
api: "/api/chat",
}),
});3. Transport Types Summary
| Transport | Package | Auto-Forwards | Use Case |
|---|---|---|---|
AssistantChatTransport | @assistant-ui/react-ai-sdk | Yes (system, tools, callSettings) | Default, recommended |
DefaultChatTransport | ai | No | Standard AI SDK usage |
DirectChatTransport | ai | No | SSR/testing with direct agent |
TextStreamChatTransport | ai | No | Plain text backends |
4. What AssistantChatTransport Forwards
When using AssistantChatTransport, the following are automatically sent to your backend:
// Your backend receives in req.body:
{
messages: UIMessage[], // Conversation messages
system: string, // System prompt from context
tools: Record<string, {...}>, // Frontend tools (as JSON schema)
callSettings: {...}, // Call settings from context
id: string, // Thread ID
trigger: string, // What triggered the request
messageId: string, // Message ID
metadata: {...}, // Request metadata
}5. Exports from @assistant-ui/react-ai-sdk
import {
// Hooks
useChatRuntime,
useAISDKRuntime,
// Transports
AssistantChatTransport,
// Helpers
frontendTools,
// Types
type UseChatRuntimeOptions,
} from "@assistant-ui/react-ai-sdk";---
Complete Migration Examples
API Route (Full Example)
Before (AI SDK v5):
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-4o"),
messages,
maxSteps: 10,
});
return (await result).toDataStreamResponse();
}After (AI SDK v6):
import {
streamText,
convertToModelMessages,
stepCountIs,
tool,
zodSchema,
} from "ai";
import type { UIMessage } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
// Allow streaming responses up to 30 seconds
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
const result = streamText({
model: openai("gpt-4o"),
messages: await convertToModelMessages(messages),
stopWhen: stepCountIs(10),
tools: {
get_weather: tool({
description: "Get the current weather",
inputSchema: zodSchema(
z.object({
city: z.string(),
}),
),
execute: async ({ city }) => {
return `The weather in ${city} is sunny`;
},
}),
},
});
return result.toUIMessageStreamResponse();
}API Route with Frontend Tools
import {
streamText,
convertToModelMessages,
stepCountIs,
tool,
zodSchema,
} from "ai";
import type { UIMessage } from "ai";
import { frontendTools } from "@assistant-ui/react-ai-sdk";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
export const maxDuration = 30;
export async function POST(req: Request) {
const {
messages,
system,
tools: clientTools,
}: {
messages: UIMessage[];
system?: string;
tools?: Record<string, { description?: string; parameters: any }>;
} = await req.json();
const result = streamText({
model: openai("gpt-4o"),
system,
messages: await convertToModelMessages(messages),
stopWhen: stepCountIs(10),
tools: {
...frontendTools(clientTools ?? {}),
search_database: tool({
description: "Search the database",
inputSchema: zodSchema(
z.object({
query: z.string(),
}),
),
execute: async ({ query }) => {
// Server-side only logic
return { results: [] };
},
}),
},
});
return result.toUIMessageStreamResponse();
}Custom Stream with Data Parts
Before (v5):
import { createDataStreamResponse, streamText } from "ai";
return createDataStreamResponse({
execute: async (writer) => {
writer.writeMessageAnnotation({
type: "custom-metadata",
timestamp: Date.now(),
});
const result = streamText({ model, messages });
result.mergeIntoDataStream(writer);
},
});After (v6):
import { createUIMessageStream, createUIMessageStreamResponse, streamText } from "ai";
const stream = createUIMessageStream({
execute: async ({ writer }) => {
// Custom data part
writer.write({
type: "data-metadata",
id: "meta-1",
data: { timestamp: Date.now() },
});
// Merge model response
const result = streamText({ model, messages: await convertToModelMessages(messages) });
writer.merge(result.toUIMessageStream());
},
onFinish: ({ messages, responseMessage }) => {
// Persist to database
},
});
return createUIMessageStreamResponse({ stream });Client Component with Tools
"use client";
import { AssistantRuntimeProvider, useAssistantTool } from "@assistant-ui/react";
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
import { z } from "zod";
function WeatherTool() {
useAssistantTool({
toolName: "get_weather",
description: "Get current weather for a location",
parameters: z.object({
location: z.string(),
}),
execute: async ({ location }) => {
// Client-side execution
const response = await fetch(`/api/weather?location=${location}`);
return response.json();
},
render: ({ args, result, status }) => {
if (status.type === "running") return <div>Loading weather...</div>;
if (result) return <WeatherCard data={result} />;
return null;
},
});
return null;
}
export function Chat() {
const runtime = useChatRuntime();
return (
<AssistantRuntimeProvider runtime={runtime}>
<WeatherTool />
{/* Your chat UI */}
</AssistantRuntimeProvider>
);
}---
Environment Configuration
Disable Warning Logging
The new warning logger outputs deprecation warnings by default. Disable with:
export AI_SDK_LOG_WARNINGS=false---
Test Utilities
V2 mock classes have been removed. Migrate to V3 equivalents:
- import { MockLanguageModelV2 } from "ai/test";
+ import { MockLanguageModelV3 } from "ai/test";
- import { MockEmbeddingModelV2 } from "ai/test";
+ import { MockEmbeddingModelV3 } from "ai/test";
- import { MockProviderV2 } from "ai/test";
+ import { MockProviderV3 } from "ai/test";---
New Utilities in v6
These are new helper functions added in v6 (not breaking changes, but useful):
import {
// Message management
pruneMessages, // Helper to prune message history by token count
safeValidateUIMessages, // Validates UI messages without throwing
// Type guards
isDataUIPart, // Type guard for data parts
// Model middleware
wrapEmbeddingModel, // Wrap embedding model with middleware
} from "ai";---
v4-Specific Changes (v4 → v6 Direct Migration)
If migrating directly from v4 (skipping v5), apply these additional changes:
Parameter Renames
- maxTokens: 1024,
+ maxOutputTokens: 1024,
- providerMetadata: { openai: { store: false } },
+ providerOptions: { openai: { store: false } },useChat Hook Overhaul
Input state is no longer managed by the hook:
// v4
- const { input, handleInputChange, handleSubmit } = useChat();
// v6
+ const [input, setInput] = useState("");
+ const { sendMessage } = useChat({
+ transport: new DefaultChatTransport({ api: "/api/chat" }),
+ });
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ sendMessage({ text: input });
+ setInput("");
+ };append → sendMessage
// v4
- append({ role: "user", content: "Hello" });
// v6
+ sendMessage({ text: "Hello" });
// Or with parts:
+ sendMessage({ parts: [{ type: "text", text: "Hello" }] });Tool Input/Output Properties
// v4
- part.args // Tool input
- part.result // Tool output
// v6
+ part.input // Tool input
+ part.output // Tool outputFile Part Changes
// v4
- part.mimeType
- part.data
// v6
+ part.mediaType
+ part.urluseAssistant Hook Removed
The useAssistant hook has been removed entirely. Use useChat with appropriate configuration instead.
Package Imports Changed
// v4
- import { useChat } from "ai/react";
// v6
+ import { useChat } from "@ai-sdk/react";
// Or for assistant-ui:
+ import { useChatRuntime } from "@assistant-ui/react-ai-sdk";Codemods for v4
Run both v5 and v6 codemods:
npx @ai-sdk/codemod upgrade # Runs all codemods (v4→v5→v6)
# OR
npx @ai-sdk/codemod v5 # v4→v5 only
npx @ai-sdk/codemod v6 # v5→v6 only---
Migration Checklist
Package Updates
- [ ] Update
aito^6.0.0 - [ ] Update
@ai-sdk/reactto^3.0.0 - [ ] Update
@ai-sdk/providerto^3.0.0 - [ ] Update
@ai-sdk/provider-utilsto^4.0.0 - [ ] Update all
@ai-sdk/*provider packages to^3.0.0 - [ ] Update
zodto^3.25.76or^4.1.8(both supported) - [ ] Update
@assistant-ui/reactto^0.14.13 - [ ] Update
@assistant-ui/react-ai-sdkto^1.3.31 - [ ] If using MCP: Install
@ai-sdk/mcpto^1.0.0
Automated Migration
- [ ] Run
npx @ai-sdk/codemod v6(from v5) - [ ] OR run
npx @ai-sdk/codemod upgrade(from v4, runs all) - [ ] Review all automated changes manually
Core Changes
- [ ] Replace
CoreMessagewithModelMessage - [ ] Replace
convertToCoreMessageswithconvertToModelMessages - [ ] Add
awaitto allconvertToModelMessages()calls - [ ] Replace
maxStepswithstopWhen: stepCountIs(n) - [ ] Update
generateObject/streamObjectto usegenerateText/streamTextwithOutput - [ ] Rename
ToolCallOptionstoToolExecutionOptions - [ ] Update embedding provider methods (
textEmbedding/textEmbeddingModel→embedding/embeddingModel) - [ ] Update tool UI helpers (
isToolUIPart→isStaticToolUIPartfor static tools) - [ ] Update
Tool.toModelOutputsignature to use destructuring - [ ] Update token usage property access paths
- [ ] Handle new "other" finish reason (was "unknown")
- [ ] Update rerank results:
relevanceScore→score - [ ] If using MCP: Update imports from
aito@ai-sdk/mcp - [ ] If using Agent: Rename
createAgentStreamResponse→createAgentUIStreamResponse - [ ] If using Agent: Rename
messages→uiMessagesin agent stream results
UI & React Changes
- [ ] Update message handling for
partsarray structure - [ ] Manage input state manually with useChat
- [ ] Replace
append()withsendMessage() - [ ] Update tool result handling: use
addToolResult({ tool, toolCallId, output })oraddToolOutputwith state - [ ] Handle new status states (submitted, streaming, ready, error)
- [ ] Update to transport-based configuration
Streaming Changes
- [ ] Update stream response:
result.toUIMessageStreamResponse()(not awaited) - [ ] Use custom data parts with
type: "data-*"pattern - [ ] Add
x-vercel-ai-ui-message-stream: v1header for custom backends - [ ] Implement
onFinishfor message persistence
Tool Changes
- [ ] Use
tool()helper for backend tool definitions - [ ] Use Zod schemas in
inputSchema(directly or withzodSchema()wrapper) - [ ] Use
frontendTools()helper for forwarding frontend tools - [ ] Move
strictJsonSchemato per-toolstrictproperty - [ ] Update tool execute signatures for
ToolExecutionOptions - [ ] Handle new tool states:
input-streaming,input-available,output-available,output-error - [ ] Place
.describe()and.meta()calls last in Zod schema chains
Structured Output
- [ ] Replace
generateObjectwithgenerateText+Output.object() - [ ] Replace
streamObjectwithstreamText+Output.object() - [ ] Use
partialOutputStreaminstead ofpartialObjectStream - [ ] Use
elementStreamfor streaming arrays
assistant-ui Specific
- [ ] Simplify client:
useChatRuntime()works with no config for/api/chat - [ ] Use
AssistantChatTransport(from @assistant-ui/react-ai-sdk) for custom endpoints - [ ] Import
DefaultChatTransportfrom "ai" package (not assistant-ui)
Provider-Specific
- [ ] Handle OpenAI
strictJsonSchemadefault change (nowtrue) - [ ] Update Azure metadata key from
openaitoazure - [ ] Update Vertex metadata key from
googletovertex - [ ] Configure Anthropic
structuredOutputModeif needed
Testing
- [ ] Update mock classes from V2 to V3
- [ ] Test all streaming functionality
- [ ] Verify tool execution with new states
- [ ] Test custom data parts
assistant-ui Version Migrations
Migrations for upgrading between assistant-ui versions.
Contents
- Version Detection
- Migration: → 0.14.x (Children API, deprecated removals)
- Migration: → 0.13.x (Top-turn anchoring)
- Migration: → 0.12.x (Unified State API)
- Migration: → 0.11.x (Runtime Rearchitecture)
- Migration: → 0.10.x (ESM Only)
- Migration: → 0.9.x (Edge Split)
- Migration: → 0.8.x (UI Split)
- Migration: → 0.7.x (Thread API)
- Migration: → 0.5.x (Runtime API)
- Migration: → 0.4.x (Message Types)
- Migration: → 0.3.x
- Migration: → 0.2.x
- Automated Search Commands
- Verification
Version Detection
npm ls @assistant-ui/react
npm view @assistant-ui/react version # LatestMigration: → 0.14.x (Children API, deprecated removals)
From 0.13.x
Two themes: APIs deprecated in v0.11/v0.12 are removed, and list primitives move from a components prop to a children render function.
Automatic migration (hook renames):
npx assistant-ui@latest upgradeRemoved hook/adapter aliases (find-and-replace):
| 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 API cleanups:
- runtime.threadList
+ runtime.threads
- 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()Custom `ChatModelAdapter`: options.config removed, use options.context.
- async run({ messages, config }) { /* config.tools, config.config, ... */ }
+ async run({ messages, context }) { /* context.tools, context.config, ... */ }State and helpers:
- useAuiState((s) => s.message.submittedFeedback)
+ useAuiState((s) => s.message.metadata.submittedFeedback)
- const original = getExternalStoreMessage(threadMessage)
+ const [original] = getExternalStoreMessages(threadMessage)
- import { toAISDKTools } from "@assistant-ui/react"
+ import { toToolsJSONSchema } from "assistant-stream"react-langgraph: useLangGraphRuntime's onSwitchToThread removed, use load.
Children render functions (the components prop still works but is deprecated):
- <ThreadPrimitive.Messages components={{ UserMessage, AssistantMessage, EditComposer }} />
+ <ThreadPrimitive.Messages>
+ {({ message }) => {
+ if (message.composer.isEditing) return <EditComposer />;
+ if (message.role === "user") return <UserMessage />;
+ return <AssistantMessage />;
+ }}
+ </ThreadPrimitive.Messages>
- <MessagePrimitive.Parts components={{ Text, tools: { Fallback: ToolFallback } }} />
+ <MessagePrimitive.Parts>
+ {({ part }) => {
+ if (part.type === "text") return <MarkdownText />;
+ if (part.type === "tool-call") return part.toolUI ?? <ToolFallback {...part} />;
+ return null;
+ }}
+ </MessagePrimitive.Parts>The same change applies to ThreadPrimitive.Suggestions, ThreadListPrimitive.Items, and ComposerPrimitive.Attachments. Returning null from the render function still renders registered tool/data UIs; return <></> to render nothing.
Search for removed patterns:
grep -rn "useAssistantApi\|useAssistantState\|useAssistantEvent\|AssistantIf\|unstable_useRemoteThreadListRuntime\|unstable_InMemoryThreadListAdapter\|getExternalStoreMessage\b\|toAISDKTools\|getModelConfig\|onSwitchToThread" --include="*.tsx" --include="*.ts"---
Migration: → 0.13.x (Top-turn anchoring)
From 0.12.x
ThreadPrimitive.ViewportSlack was removed; top-anchor registration is now automatic on MessagePrimitive.Root when turnAnchor="top". Replace fillClampThreshold / fillClampOffset with topAnchorMessageClamp on ThreadPrimitive.Viewport:
- <ThreadPrimitive.ViewportSlack fillClampThreshold="10em" fillClampOffset="6em">
- ...
- </ThreadPrimitive.ViewportSlack>
+ <ThreadPrimitive.Viewport
+ turnAnchor="top"
+ topAnchorMessageClamp={{ tallerThan: "10em", visibleHeight: "6em" }}
+ >
+ ...
+ </ThreadPrimitive.Viewport>---
Migration: → 0.12.x (Unified State API)
From 0.11.x
Unified state API replaces individual context hooks.
Automatic migration available:
npx assistant-ui@latest upgradeAssistant API hooks renamed (the old names were removed in v0.14):
- import { useAssistantApi, useAssistantState, useAssistantEvent, AssistantIf } from "@assistant-ui/react";
+ import { useAui, useAuiState, useAuiEvent, AuiIf } from "@assistant-ui/react";
- const api = useAssistantApi();
+ const aui = useAui();
- const messages = useAssistantState(s => s.thread.messages);
+ const messages = useAuiState(s => s.thread.messages);
- useAssistantEvent("thread.run-start", callback);
+ useAuiEvent("thread.runStart", callback);
- <AssistantIf condition={...}>
+ <AuiIf condition={...}>Context hooks replaced with unified state API:
All individual context hooks replaced by useAuiState / useAui:
- const { messages } = useThread();
+ const messages = useAuiState(s => s.thread.messages);
- const runtime = useThreadRuntime();
+ const thread = useAui().thread();
- const { isEditing } = useComposer();
+ const isEditing = useAuiState(s => s.composer.isEditing);
- const runtime = useComposerRuntime();
+ const composer = useAui().composer();
- const { status } = useMessage();
+ const status = useAuiState(s => s.message.status);
- const runtime = useMessageRuntime();
+ const message = useAui().message();Other deprecated hooks: useAssistantRuntime, useEditComposer, useThreadListItem, useThreadListItemRuntime, useMessagePart, useMessagePartRuntime, useAttachment, useAttachmentRuntime, useThreadModelContext, useThreadComposer, useThreadList.
Event names changed to camelCase:
| 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 |
Unchanged: thread.initialize, composer.send.
`thread().composer()` invocation (0.12.11):
- aui.thread().composer.send();
+ aui.thread().composer().send();`submitMode` prop (0.12.10) — deprecates `submitOnEnter`:
"enter"(default) — submit on Enter"ctrlEnter"— submit on Ctrl/Cmd+Enter, plain Enter for newlines"none"— disable keyboard submission
Zod: AI SDK v6 (used by @assistant-ui/react-ai-sdk 1.3.x) requires zod@^3.25.76 || ^4.1.8; both Zod 3.25+ and Zod 4 work.
New primitives:
ChainOfThoughtPrimitive(0.12.8)SelectionToolbarPrimitive(0.12.10)SuggestionPrimitive(0.12.3)
`@assistant-ui/core` extraction (0.12.11):
- Framework-agnostic core extracted to
@assistant-ui/core - Shared React code in
@assistant-ui/core/react(re-exported by@assistant-ui/reactand@assistant-ui/react-native)
Search for deprecated patterns:
grep -rn "useAssistantApi\|useAssistantState\|useAssistantEvent\|AssistantIf\|submitOnEnter\|useThread()\|useComposer()\|useMessage()\|useThreadRuntime\|useComposerRuntime\|useMessageRuntime" --include="*.tsx" --include="*.ts"---
Migration: → 0.11.x (Runtime Rearchitecture)
From 0.10.x
New unified state API (hooks renamed to useAui/useAuiState/useAuiEvent in 0.12.x):
import {
useAssistantApi,
useAssistantState,
useAssistantEvent
} from "@assistant-ui/react";
// State access (replaces various useThread* hooks)
const messages = useAssistantState(s => s.thread.messages);
const isRunning = useAssistantState(s => s.thread.isRunning);
const api = useAssistantApi();
api.thread().append({ role: "user", content: [{ type: "text", text: "Hello" }] });
api.thread().cancelRun();
useAssistantEvent("composer.send", (e) => {
console.log("Message sent:", e.messageId);
});AI SDK v5/v6 support added:
- Use
useChatRuntimefor AI SDK v6 useAISDKRuntimestill works for migration
Renames:
toolUIs→tools(0.11.39)useLocalThreadRuntimedeprecated, useuseLocalRuntime
---
Migration: → 0.10.x (ESM Only)
From 0.9.x
BREAKING: CommonJS dropped
Update bundler if needed:
// package.json
{
"type": "module"
}Or configure bundler for ESM:
// next.config.js
export default {
experimental: {
esmExternals: true
}
}New APIs:
ContentPartrenamed toMessagePart(0.10.25)MessageContent.ToolGroupaddedruntime.thread.reset()added
---
Migration: → 0.9.x (Edge Split)
From 0.8.x
Edge package split:
- Edge runtime utilities moved to separate entry points
- Check imports if using edge runtime
---
Migration: → 0.8.x (UI Split)
From 0.7.x
BREAKING: Pre-styled UI moved out of `@assistant-ui/react`
0.7.x: Thread etc. were re-exported from @assistant-ui/react via ./ui subpath 0.8.0+: Use shadcn/ui registry (recommended) or @assistant-ui/react-ui (legacy, not maintained)
Option 1: shadcn/ui Registry (Recommended)
# Using assistant-ui CLI
npx assistant-ui add thread thread-list
# Or using shadcn CLI
npx shadcn@latest add "https://r.assistant-ui.com/thread"Components are copied to your project (e.g., components/assistant-ui/thread.tsx).
// Styled components - now local files
// Note: ThreadWelcome is now embedded inside Thread (shows when thread is empty)
- import { Thread, ThreadWelcome } from "@assistant-ui/react";
+ import { Thread } from "@/components/assistant-ui/thread";
// Primitives remain in @assistant-ui/react (no change)
import { ThreadPrimitive } from "@assistant-ui/react";Option 2: Legacy Package (Not Recommended)
@assistant-ui/react-ui exists but is not actively maintained.
Search for imports to update:
grep -r "from ['\"]@assistant-ui/react['\"]" --include="*.tsx" --include="*.ts" | grep -v "Primitive"setResult/setArtifact merged (0.8.18):
- tool.setResult(result);
- tool.setArtifact(artifact);
+ tool.setResponse({ result, artifact });---
Migration: → 0.7.x (Thread API)
From 0.6.x or 0.5.x
BREAKING (0.7.44): Thread API moved
- runtime.switchToThread(threadId);
+ runtime.threads.switchToThread(threadId);
- runtime.switchToNewThread();
+ runtime.threads.switchToNewThread();
- runtime.threadList
+ runtime.threadsSearch:
grep -r "runtime\.switchToThread\|runtime\.switchToNewThread\|runtime\.threadList" --include="*.tsx" --include="*.ts"Deprecated features dropped (0.7.0):
- All previously deprecated APIs removed
ThreadListItemPrimitiveintroduced
---
Migration: → 0.5.x (Runtime API)
From 0.4.x
maxToolRoundtrips → maxSteps (0.5.74):
- maxToolRoundtrips: 5,
+ maxSteps: 5,New Runtime API introduced (0.5.61+):
ThreadRuntime.Composer- Status/attachments/metadata on all messages
---
Migration: → 0.4.x (Message Types)
From 0.3.x
BREAKING: Message type renames
- import type { AssistantMessage, UserMessage } from "@assistant-ui/react";
+ import type { ThreadAssistantMessage, ThreadUserMessage } from "@assistant-ui/react";Search:
grep -r "AssistantMessage\|UserMessage" --include="*.tsx" --include="*.ts" | grep -v "Thread"System message support added
---
Migration: → 0.3.x
From 0.2.x
BREAKING: Message.InProgress dropped
- Use message status instead of
Message.InProgress
---
Migration: → 0.2.x
From 0.1.x
BREAKING: MessagePartText renders as `<p>`
- Text parts now wrapped in paragraph element
- Adjust CSS if needed
---
Automated Search Commands
Find patterns that need updating:
# Old thread API
grep -rn "runtime\.switchToThread\|runtime\.threadList" --include="*.tsx" --include="*.ts"
# Old message types
grep -rn "AssistantMessage\[^C\]\|UserMessage\[^C\]" --include="*.tsx" --include="*.ts"
# Old tool API
grep -rn "setResult\|setArtifact" --include="*.tsx" --include="*.ts"
# Styled imports (need shadcn registry migration)
grep -rn "from ['\"]@assistant-ui/react['\"]" --include="*.tsx" | grep -v "Primitive\|Runtime\|use"Verification
After migration:
# Type check
npx tsc --noEmit
# Build
pnpm build
# Test
pnpm testManual verification:
- [ ] App starts
- [ ] Chat renders
- [ ] Messages send/receive
- [ ] Tools work
- [ ] Thread switching works
Breaking Changes Quick Reference
Fast lookup for breaking changes by version.
By Version
| Version | Breaking Change | Migration |
|---|---|---|
| 0.14.0 | components prop → children render functions; deprecated hooks/aliases removed | Children render functions; useAui/useAuiState/useAuiEvent/AuiIf; drop unstable_ prefixes |
| 0.13.0 | ThreadPrimitive.ViewportSlack removed | Use topAnchorMessageClamp on ThreadPrimitive.Viewport |
| 0.12.0 | Unified state API | Use useAui, useAuiState, useAuiEvent, AuiIf |
| 0.11.0 | Runtime rearchitecture | Use useAssistantApi/useAssistantState (renamed to useAui/useAuiState in 0.12) |
| 0.10.0 | CommonJS dropped | Use ESM, set "type": "module" |
| 0.8.18 | setResult/setArtifact merged | Use setResponse({ result, artifact }) |
| 0.8.0 | UI moved out of core | Use shadcn registry (recommended) or primitives |
| 0.7.44 | runtime.switchToThread() moved | Use runtime.threads.switchToThread() |
| 0.7.44 | runtime.threadList renamed | Use runtime.threads |
| 0.7.0 | Deprecated features dropped | Update to non-deprecated APIs |
| 0.5.74 | maxToolRoundtrips renamed | Use maxSteps |
| 0.4.0 | AssistantMessage renamed | Use ThreadAssistantMessage |
| 0.4.0 | UserMessage renamed | Use ThreadUserMessage |
| 0.3.0 | Message.InProgress dropped | Use message status |
| 0.2.0 | MessagePartText renders as <p> | Adjust CSS |
By Pattern
Import Changes
# Styled components (0.8.0+) - use shadcn registry (recommended)
- import { Thread } from "@assistant-ui/react";
+ import { Thread } from "@/components/assistant-ui/thread";
# Note: Run `npx assistant-ui add thread` to install
# Message types (0.4.0+)
- import type { AssistantMessage, UserMessage } from "@assistant-ui/react";
+ import type { ThreadAssistantMessage, ThreadUserMessage } from "@assistant-ui/react";
# AI SDK v6 (react-ai-sdk 1.0+)
- import { useChat } from "ai/react";
- import { useAISDKRuntime } from "@assistant-ui/react-ai-sdk";
+ import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/react-ai-sdk";API Changes
# Thread switching (0.7.44+)
- runtime.switchToThread(id);
- runtime.switchToNewThread();
- runtime.threadList
+ runtime.threads.switchToThread(id);
+ runtime.threads.switchToNewThread();
+ runtime.threads
# Tool response (0.8.18+)
- tool.setResult(result);
- tool.setArtifact(artifact);
+ tool.setResponse({ result, artifact });
# State access (0.11.0+)
- const { messages } = useThread();
+ const messages = useAuiState(s => s.thread.messages);
# Actions (0.11.0+)
- useThreadActions().append(...)
+ useAui().thread().append(...)Config Changes
# Tool steps (0.5.74+)
- maxToolRoundtrips: 5,
+ maxSteps: 5,Search Commands
Find code needing updates:
# All breaking patterns
grep -rn "runtime\.switchToThread\|runtime\.threadList\|AssistantMessage[^C]\|UserMessage[^C]\|setResult\|setArtifact\|maxToolRoundtrips" --include="*.tsx" --include="*.ts"
# Specific version checks
grep -rn "from ['\"]@assistant-ui/react['\"]" --include="*.tsx" | grep -v Primitive # 0.8.0
grep -rn "Message\.InProgress" --include="*.tsx" # 0.3.0AI SDK v6 Changes (Separate)
See ./ai-sdk-v6.md for AI SDK specific migrations:
| Old | New |
|---|---|
maxSteps | stopWhen: stepCountIs(n) |
parameters | inputSchema (in tool()) |
toDataStreamResponse() | toUIMessageStreamResponse() |
generateObject() | generateText() + Output.object() |
CoreMessage | ModelMessage |
Message | UIMessage |
Version Compatibility
Current latest: @assistant-ui/react 0.14.x, @assistant-ui/react-ai-sdk 1.3.x.
| @assistant-ui/react | react-ai-sdk | AI SDK | Zod |
|---|---|---|---|
| 0.14.x | 1.3.x | 6.x | 3.25+ or 4.x |
| 0.12.x to 0.13.x | 1.3.x | 6.x | 3.25+ or 4.x |
| 0.11.x | 1.2.x | 6.x | 3.25+ or 4.x |
| 0.10.x | 0.x | 4.x to 5.x | 3.x |
| 0.8.x to 0.9.x | 0.x | 4.x | 3.x |
| < 0.8.0 | 0.x | 4.x | 3.x |
Related skills
How it compares
Pick update for full AI SDK v6 migration with verification gates; use framework docs alone only for trivial single-import changes.
FAQ
What migration order does update follow?
AI SDK first if below 6.0.0, then assistant-ui, then type check and build verification.
When should I use setup instead of update?
For a first-time install or fresh scaffold, not when upgrading an existing project.
How does update detect current versions?
npm ls for installed packages and npm view for latest published versions on npm.
Is Update safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.