ai-sdk-ui
Build React chat interfaces with Vercel AI SDK v6. Covers useChat/useCompletion/useObject hooks, message parts structure, tool approval workflows, and 18 UI error solutions. Prevents documented issues with React Strict Mode, concurrent requests, stale closures, and tool approval edge cases. Use when: implementing AI chat UIs, migrating v5→v6, troubleshooting "useChat failed to parse stream", "stale body values", "React maximum update depth", "Cannot read properties of undefined (reading 'state')", or tool approval workflow errors.
Install
mkdir -p .claude/skills/ai-sdk-ui && curl -L -o skill.zip "https://mcp.directory/api/skills/download/9084" && unzip -o skill.zip -d .claude/skills/ai-sdk-ui && rm skill.zipInstalls to .claude/skills/ai-sdk-ui
About this skill
AI SDK UI - Frontend React Hooks
Frontend React hooks for AI-powered user interfaces with Vercel AI SDK v6.
Version: AI SDK v6.0.42 (Stable) Framework: React 18+/19, Next.js 14+/15+ Last Updated: 2026-01-20
AI SDK v6 Stable (January 2026)
Status: Stable Release Latest: [email protected], @ai-sdk/[email protected], @ai-sdk/[email protected] Migration: Minimal breaking changes from v5 → v6
New UI Features in v6
1. Message Parts Structure (Breaking Change)
In v6, message content is now accessed via .parts array instead of .content:
// ❌ v5 (OLD)
{messages.map(m => (
<div key={m.id}>{m.content}</div>
))}
// ✅ v6 (NEW)
{messages.map(m => (
<div key={m.id}>
{m.parts.map((part, i) => {
if (part.type === 'text') return <span key={i}>{part.text}</span>;
if (part.type === 'tool-invocation') return <ToolCall key={i} tool={part} />;
if (part.type === 'file') return <FilePreview key={i} file={part} />;
return null;
})}
</div>
))}
Part Types:
text- Text content with.textpropertytool-invocation- Tool calls with.toolName,.args,.resultfile- File attachments with.mimeType,.datareasoning- Model reasoning (when available)source- Source citations
3. Agent Integration
Type-safe messaging with agents using InferAgentUIMessage<typeof agent>:
import { useChat } from '@ai-sdk/react';
import type { InferAgentUIMessage } from 'ai';
import { myAgent } from './agent';
export default function AgentChat() {
const { messages, sendMessage } = useChat<InferAgentUIMessage<typeof myAgent>>({
api: '/api/chat',
});
// messages are now type-checked against agent schema
}
4. Tool Approval Workflows (Human-in-the-Loop) Request user confirmation before executing tools:
import { useChat } from '@ai-sdk/react';
import { useState } from 'react';
export default function ChatWithApproval() {
const { messages, sendMessage, addToolApprovalResponse } = useChat({
api: '/api/chat',
});
const handleApprove = (toolCallId: string) => {
addToolApprovalResponse({
toolCallId,
approved: true, // or false to deny
});
};
return (
<div>
{messages.map(message => (
<div key={message.id}>
{message.toolInvocations?.map(tool => (
tool.state === 'awaiting-approval' && (
<div key={tool.toolCallId}>
<p>Approve tool call: {tool.toolName}?</p>
<button onClick={() => handleApprove(tool.toolCallId)}>
Approve
</button>
<button onClick={() => addToolApprovalResponse({
toolCallId: tool.toolCallId,
approved: false
})}>
Deny
</button>
</div>
)
))}
</div>
))}
</div>
);
}
5. Auto-Submit Capability Automatically continue conversation after handling approvals:
import { useChat, lastAssistantMessageIsCompleteWithApprovalResponses } from '@ai-sdk/react';
export default function AutoSubmitChat() {
const { messages, sendMessage } = useChat({
api: '/api/chat',
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,
// Automatically resubmit after all approval responses provided
});
}
6. Structured Output in Chat
Generate structured data alongside tool calling (previously only available in useObject):
import { useChat } from '@ai-sdk/react';
import { z } from 'zod';
const schema = z.object({
summary: z.string(),
sentiment: z.enum(['positive', 'neutral', 'negative']),
});
export default function StructuredChat() {
const { messages, sendMessage } = useChat({
api: '/api/chat',
// Server can now stream structured output with chat messages
});
}
useChat Hook - v4 → v5 Breaking Changes
CRITICAL: useChat no longer manages input state in v5!
v4 (OLD - DON'T USE):
const { messages, input, handleInputChange, handleSubmit, append } = useChat();
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
</form>
v5 (NEW - CORRECT):
const { messages, sendMessage } = useChat();
const [input, setInput] = useState('');
<form onSubmit={(e) => {
e.preventDefault();
sendMessage({ content: input });
setInput('');
}}>
<input value={input} onChange={(e) => setInput(e.target.value)} />
</form>
Summary of v5 Changes:
- Input management removed:
input,handleInputChange,handleSubmitno longer exist append()→sendMessage(): New method for sending messagesonResponseremoved: UseonFinishinsteadinitialMessages→ controlled mode: Usemessagesprop for full controlmaxStepsremoved: Handle on server-side only
See references/use-chat-migration.md for complete migration guide.
useAssistant Hook (Deprecated)
⚠️ Deprecation Notice:
useAssistantis deprecated as of AI SDK v5. OpenAI Assistants API v2 will sunset on August 26, 2026. For new projects, useuseChatwith custom backend logic instead. See the openai-assistants skill for migration guidance.
Interact with OpenAI-compatible assistant APIs with automatic UI state management.
Import:
import { useAssistant } from '@ai-sdk/react';
Basic Usage:
'use client';
import { useAssistant } from '@ai-sdk/react';
import { useState, FormEvent } from 'react';
export default function AssistantChat() {
const { messages, sendMessage, isLoading, error } = useAssistant({
api: '/api/assistant',
});
const [input, setInput] = useState('');
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
sendMessage({ content: input });
setInput('');
};
return (
<div>
{messages.map(m => (
<div key={m.id}>
<strong>{m.role}:</strong> {m.content}
</div>
))}
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
disabled={isLoading}
/>
</form>
{error && <div>{error.message}</div>}
</div>
);
}
Use Cases:
- Building OpenAI Assistant-powered UIs
- Managing assistant threads and runs
- Streaming assistant responses with UI state management
- File search and code interpreter integrations
See official docs for complete API reference: https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-assistant
Top UI Errors & Solutions
See references/top-ui-errors.md for complete documentation. Quick reference:
1. useChat Failed to Parse Stream
Error: SyntaxError: Unexpected token in JSON at position X
Cause: API route not returning proper stream format.
Solution:
// ✅ CORRECT
return result.toDataStreamResponse();
// ❌ WRONG
return new Response(result.textStream);
2. useChat No Response
Cause: API route not streaming correctly.
Solution:
// App Router - use toDataStreamResponse()
export async function POST(req: Request) {
const result = streamText({ /* ... */ });
return result.toDataStreamResponse(); // ✅
}
// Pages Router - use pipeDataStreamToResponse()
export default async function handler(req, res) {
const result = streamText({ /* ... */ });
return result.pipeDataStreamToResponse(res); // ✅
}
3. Streaming Not Working When Deployed
Cause: Deployment platform buffering responses.
Solution: Vercel auto-detects streaming. Other platforms may need configuration.
4. Stale Body Values with useChat
Cause: body option captured at first render only.
Solution:
// ❌ WRONG - body captured once
const { userId } = useUser();
const { messages } = useChat({
body: { userId }, // Stale!
});
// ✅ CORRECT - use controlled mode
const { userId } = useUser();
const { messages, sendMessage } = useChat();
sendMessage({
content: input,
data: { userId }, // Fresh on each send
});
5. React Maximum Update Depth
Cause: Infinite loop in useEffect.
Solution:
// ❌ WRONG
useEffect(() => {
saveMessages(messages);
}, [messages, saveMessages]); // saveMessages triggers re-render!
// ✅ CORRECT
useEffect(() => {
saveMessages(messages);
}, [messages]); // Only depend on messages
See references/top-ui-errors.md for 13 more common errors (18 total documented).
Streaming Best Practices
Performance
Always use streaming for better UX:
// ✅ GOOD - Streaming (shows tokens as they arrive)
const { messages } = useChat({ api: '/api/chat' });
// ❌ BAD - Non-streaming (user waits for full response)
const response = await fetch('/api/chat', { method: 'POST' });
UX Patterns
Show loading states:
{isLoading && <div>AI is typing...</div>}
Provide stop button:
{isLoading && <button onClick={stop}>Stop</button>}
Auto-scroll to latest message:
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
Disable input while loading:
<input disabled={isLoading} />
See references/streaming-patterns.md for comprehensive best practices.
React Strict Mode Considerations
React Strict Mode intentionally double-invokes effects to catch bugs. When using useChat or useCompletion in effects (auto-resume, initial messages), guard against double execution to prevent duplicate API calls and token waste.
Problem:
'use client';
import { useChat } from '@ai-sdk/react';
import { useEffect } from 'react';
export default function Chat() {
const { messages, sendMessage, resumeStream } = useChat({
api: '/api/chat',
resume: true,
});
useEffect(() => {
// ❌ Triggers twice in strict mode → two concurrent streams
sendMessage({ content: 'Hello' });
// or
re
---
*Content truncated.*
More by openclaw
View all skills by openclaw →You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
pdf-to-markdown
aliceisjustplaying
Convert entire PDF documents to clean, structured Markdown for full context loading. Use this skill when the user wants to extract ALL text from a PDF into context (not grep/search), when discussing or analyzing PDF content in full, when the user mentions "load the whole PDF", "bring the PDF into context", "read the entire PDF", or when partial extraction/grepping would miss important context. This is the preferred method for PDF text extraction over page-by-page or grep approaches.
Related MCP Servers
Browse all serversAI-ready access to Grafana UI: full React component library—TypeScript source, MDX docs, Storybook examples, tests, and
Build persistent semantic networks for enterprise & engineering data management. Enable data persistence and memory acro
The fullstack MCP framework for developing MCP apps for ChatGPT, Claude, and building MCP servers for AI agents. Connect
Solana Agent Kit: Easily deploy tokens, mint NFTs, and manage DeFi & cross-chain tasks with Solana integration in chat i
Connect MongoDB databases to chat interfaces. Manage AWS with MongoDB, explore Atlas cost, and inspect collections secur
dbt bridges data build tool resources and natural language, enabling top BI software features, metadata discovery, and d
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.