Chat - Minimal core chat
Wire up a minimal headless chat using ChatProvider and the useChat() hook.
The minimal example demonstrates the core pattern:
ChatProviderowns the runtime and wraps the component tree.useChat()reads messages and streaming state in one call- A plain input and button trigger
sendMessage(). - the assistant response streams back through the adapter
Everything else—layout, styling, message rendering—is plain React with no framework opinions.
Key concepts
Defining an adapter
The adapter is the only required prop on ChatProvider.
At minimum, it implements sendMessage() and returns a ReadableStream of chunks:
const adapter: ChatAdapter = {
async sendMessage({ message }) {
return createChunkStream(
createTextResponseChunks(
`response-${message.id}`,
`You said: "${getMessageText(message)}".`,
),
{ delayMs: 220 },
);
},
};
Wiring the provider
Wrap the component tree with ChatProvider and pass the adapter:
<ChatProvider adapter={adapter} initialActiveConversationId="support">
<MinimalChatInner />
</ChatProvider>
initialActiveConversationId sets the initial conversation without requiring controlled state.
Reading state from the runtime
Inside ChatProvider, call useChat() to get messages, streaming state, and actions:
const { messages, sendMessage, isStreaming } = useChat();
Minimal headless chat
Idle
Send the first message to start the thread.
Press Enter to start editing
Key takeaways
- The adapter is the only backend integration point—the runtime handles everything else.
useChat()provides both state and actions in a single hook- No CSS, no components, no design system required—core is pure runtime.
See also
- Hooks for details on the full hook API.
- Adapters for details on writing real adapters.
- Controlled state for details on owning state externally.
- Selector-driven thread for details on efficient large threads.