useAIChat
SDK-agnostic chat adapter hook. Thin wrapper over AI SDK v5's useChat that normalizes the return shape to Hex types: message role narrowed to user/assistant/system/tool, text parts concatenated into a content string, and a composer slice that spreads directly onto <Composer>. Manages input state locally (AI SDK v5 no longer owns it). Future LangChain / Mastra adapters return the same shape.
Installation
pnpm dlx @hex-core/cli add use-ai-chatUsage
import { useAIChat } from "@/components/ui/use-ai-chat"Minimal chatbot
Wire useAIChat to Composer and MessageList. The hook owns input state, message normalization, and abort/retry handles.
import { useAIChat, Composer, MessageList, Message } from "@hex-core/components";
function Chatbot() {
const chat = useAIChat({ api: "/api/chat" });
return (
<div className="flex flex-col gap-4">
<MessageList>
{chat.messages.map((m) => (
<Message key={m.id} role={m.role}>{m.content}</Message>
))}
</MessageList>
<Composer {...chat.composer} placeholder="Ask anything..." />
</div>
);
}API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
api | string | — | Chat endpoint URL. Defaults to /api/chat per AI SDK convention. |
initialMessages | object | — | ReadonlyArray<HexUIMessage>. Useful for restoring conversations from server-side state. |
sdkOptions | object | — | Record<string, unknown>. Escape hatch for AI SDK options not yet promoted to the public surface (onError, onFinish, body, headers, etc). |
AI Guidance
When to use
Use when wiring a chatbot UI that streams responses from a server endpoint. Spread chat.composer onto <Composer> and iterate chat.messages into <Message> bubbles. Pair with useStreamingMessage when you need per-bubble loading indicators or retry buttons.
When not to use
Don't use for non-streaming completions — useCompletion from @ai-sdk/react is closer. Don't use for stateful agent workflows that need tool approvals; that surface lands in Phase 5.
Common mistakes
- Reading chat.messages[i].content and ignoring chat.messages[i].parts — tool calls and reasoning blocks live in parts; content only carries concatenated text.
- Forgetting that AI SDK v5 manages messages via sendMessage({ text }) — the hook handles this for you via composer.onSubmit. Don't call sdk.handleSubmit directly.
- Wiring chat.composer.disabled to your own state — the hook already disables during submitted/streaming. Layer additional checks with &&, don't replace.
Accessibility
No DOM output — accessibility lives in the components this hook drives. Composer manages aria-label on the textarea; MessageList renders a live region.
Related components
Token budget: 789
Verified against @hex-core/components@1.14.0