Skip to content
+

Chat - Minimal core chat

Start with the smallest working ChatProvider and useChat() setup.

This demo keeps the UI intentionally small to demonstrate the core pattern:

  • ChatProvider owns the runtime and wraps your 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 ChatProvider

Wrap your 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 with useChat()

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