Skip to content
+

Chat - Minimal core chat

Wire up a minimal headless chat using ChatProvider and the useChat() hook.

The minimal example demonstrates the core pattern:

  • ChatProvider owns 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

API