Interop
A drop-in for the AI SDK
skeehn renders @ai-sdk/react’s useChat().messages out of the box. Pass them to <Conversation> and every UIMessage part — text, reasoning, tool calls, sources, files — maps to the right skeehn component. No glue code, and zero hard dependency on ai: the types are structural, so your bundle stays clean and the native zero-dep path still works.
Live — rendered from a real UIMessage[] transcript
Your component
Wire useChat to your endpoint and hand the messages to <Conversation>. It autoscrolls while streaming and shows a “jump to latest” affordance when you scroll up.
'use client';
import { useChat } from '@ai-sdk/react';
import { Conversation } from '@skeehn/react/ai';
import '@skeehn/core/engine.css';
export function Chat() {
const { messages, sendMessage } = useChat();
// skeehn maps every UIMessage.part to the right component for you:
return <Conversation messages={messages} />;
}Your route handler
Standard AI SDK v5 — stream a model and return a UIMessage stream. skeehn renders whatever it emits.
// app/api/chat/route.ts
import { streamText, convertToModelMessages } from 'ai';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: 'anthropic/claude-sonnet-4-6', // via the AI Gateway
messages: convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse(); // emits UIMessage parts
}How parts map to components
| UIMessage part | skeehn component |
|---|---|
| text | ChatBubble (Markdown for assistant prose) |
| reasoning | ThinkingBlock |
| tool-* / dynamic-tool | ToolCard (input · output · error) |
| source-url / source-document | CitationCard |
| file (image/*) | <img> · otherwise FileAttachment |
| step-start | Divider between steps |
| data-* | your renderData() override |
Customize any part
Override a part type, or plug in your markdown renderer (react-markdown, streamdown) — everything else keeps skeehn’s defaults.
<Conversation
messages={messages}
renderMarkdown={(text) => <Streamdown>{text}</Streamdown>}
components={{
tool: (part) => <MyToolCard part={part} />,
}}
/>Composed primitives
Build your own surface from the same parts: <Message>, <MessageActions> (copy · regenerate · edit · feedback), <Sources>, <ModelPicker>, and <ScrollToBottomButton> — all token-themed across every skeehn theme.