Skip to content
+

Chat - Core

Runtime, adapter contract, hooks, selectors, and stream processing for building fully custom chat UIs.

@mui/x-chat/headless is the runtime-focused layer in the chat package family. It gives you chat state, streaming, adapters, selectors, and composer logic without imposing any rendered UI.

Where core fits

Use the core layer when you want chat behavior but you want to own the markup, layout, styling, and interaction model yourself.

Core owns:

  • the normalized chat store
  • controlled and uncontrolled state models
  • adapter-driven message sending and history loading
  • stream processing and runtime callbacks
  • realtime subscriptions and presence/read updates
  • selector-friendly hooks for render-sensitive trees
  • composer state, attachments, and submit behavior
  • typed message-part and stream contracts

Core does not own:

  • semantic DOM structure
  • default rendering for message parts
  • visual slots or compound components
  • Material UI theming or styling

Those concerns belong to Headless.

Mental model

The following demo shows the core runtime with a minimal custom UI:

Minimal headless chat
Idle

Send the first message to start the thread.

The runtime is easiest to understand as a pipeline:

  1. ChatProvider creates the store and runtime actions.
  2. Your ChatAdapter talks to the backend and returns streams.
  3. processStream turns chunks into normalized message updates.
  4. Hooks and selectors read the store at the granularity your UI needs.
  5. Your components render plain React, headless primitives, or your own design system.
<ChatProvider adapter={adapter}>
  <YourCustomChatUI />
</ChatProvider>

The main public surface is:

import {
  ChatProvider,
  chatSelectors,
  useChat,
  useChatComposer,
  useChatPartRenderer,
  useChatStatus,
  useChatStore,
  useConversation,
  useConversations,
  useMessage,
  useMessageIds,
} from '@mui/x-chat/headless';

State and store

ChatProvider supports both controlled and uncontrolled state for messages, conversations, active conversation ID, and composer value. Internally the store normalizes data into ID arrays and record maps, making streaming updates efficient and selector subscriptions granular.

See State and store for the full ChatProvider props reference, the controlled/uncontrolled model, error model, callbacks, and store internals.

Hooks

Nine public hooks cover the full spectrum from all-in-one orchestration (useChat()) to row-level subscriptions (useMessageIds() + useMessage(id)).

Narrower hooks like useChatComposer(), useChatStatus(), and useChatPartRenderer() handle specific concerns without subscribing to unrelated state.

See Hooks for signatures, return types, and when-to-use guidance for every hook.

Selectors

chatSelectors provides memoized selectors for the normalized store. Use them directly with useChatStore() for custom derived values and advanced subscriptions.

See Selectors for the full selector table and usage patterns.

Adapter contract

ChatAdapter is the transport boundary between the runtime and your backend. Only sendMessage() is required — everything else is optional and incrementally adopted. The adapter works with HTTP, SSE, WebSocket, or AI SDK-style streaming backends.

See Adapters for the full interface reference, a step-by-step writing guide, and backend pattern guidance.

Streaming

The adapter returns a ReadableStream of typed chunks. The runtime processes text, reasoning, tool, source, file, data, step, and metadata chunks into normalized message parts. streamFlushInterval batches rapid deltas for efficient store updates.

See Streaming for the chunk protocol reference, envelope format, and stream construction patterns.

Realtime

The adapter's subscribe() method enables push-based updates for messages, conversations, typing indicators, presence, and read state. The runtime manages the subscription lifecycle on mount and unmount.

See Realtime for event types, store effects, and consumption patterns.

Type augmentation

Module augmentation lets you add app-specific metadata, typed tool definitions, typed data-* payloads, and custom message parts. Types flow through the entire stack at compile time — no provider props needed.

See Type augmentation for the six registry interfaces, a step-by-step guide, and an end-to-end example.

Core examples

The core examples are organized as demos. Move from the smallest setup to the more specialized runtime patterns:

Scope boundary

  • Stay on core when you want runtime primitives and complete control over UI.
  • Move to Headless when you want structure and composition primitives.

API