Skip to content
+

Chat - Thread

Compose the active conversation surface from themed thread components, override individual slots, and recompose the layout using context hooks.

The thread pane is the single-conversation view in a chat interface. It combines a header area, a scrollable message log, and a composer into one cohesive surface. @mui/x-chat ships each region as a themed component built with styled() and Material UI theme tokens.

The following demo shows the thread in action:

MUI Assistant
Material UI chat
Styled with your active MUI theme
Material UI chat

Styled with your active MUI theme

MUI Assistant
MUI Assistant

Hello! I am styled using your active Material UI theme. Try sending a message.

You
You

Great — the bubble colors come from palette.primary and the typography from the theme.

Component anatomy

ChatConversation                    ← thread shell, derives the active conversation
  ChatConversationHeader            ← header bar with divider styling
    ChatConversationTitle           ← conversation name
    ChatConversationSubtitle        ← secondary line (participants, presence, etc.)
    ChatConversationHeaderActions   ← action area (archive, mute, context menu)
  ChatMessageList                   ← virtualized scrollable message area
    ChatMessageGroup                ← groups consecutive same-author messages
      ChatMessage                   ← individual message row (grid layout)
        ChatMessageAvatar           ← author avatar
        ChatMessageContent          ← bubble + inner part renderers
        ChatMessageMeta             ← timestamp, status indicator
        ChatMessageActions          ← hover action menu
  ChatComposer             ← composer form (border-top)
    ChatComposerTextArea   ← auto-resizing textarea
    ChatComposerToolbar    ← button row
      ChatComposerAttachButton
      ChatComposerSendButton

All components are exported from @mui/x-chat.

Header anatomy

ChatConversationHeader is a <header> element with divider styling. It reads the active conversation through context so every child has access to the same conversation state without additional wiring.

import {
  ChatConversation,
  ChatConversationHeader,
  ChatConversationTitle,
  ChatConversationSubtitle,
  ChatConversationHeaderActions,
} from '@mui/x-chat';

ownerState and how state flows

ChatConversation owns the conversation-level ownerState, and the header subcomponents inherit that same state through the slot system.

Conversation ownerState

Field Type Description
conversationId string | undefined Currently selected conversation ID
conversation ChatConversation | null Full active conversation object, when loaded
hasConversation boolean Whether the thread currently has a selection

The hasConversation flag is particularly useful for hiding action buttons or showing a placeholder when no conversation is active. ChatConversationHeader, ChatConversationTitle, ChatConversationSubtitle, and ChatConversationHeaderActions all receive this same conversation-level state.

Overriding a header slot

The most targeted customization is to replace the element type on one slot while keeping everything else:

MUI Assistant
Material UI chat
Styled with your active MUI theme
Material UI chat

Styled with your active MUI theme

MUI Assistant
MUI Assistant

Hello! I am styled using your active Material UI theme. Try sending a message.

You
You

Great — the bubble colors come from palette.primary and the typography from the theme.

Using ownerState in a custom title

The ownerState prop received by slot components carries the full conversation context. This makes it straightforward to render dynamic content derived from the active conversation:

import { ChatConversationTitle } from '@mui/x-chat';

const LiveTitle = React.forwardRef(function LiveTitle(
  { ownerState, ...props },
  ref,
) {
  const memberCount = ownerState?.conversation?.metadata?.memberCount;
  return (
    <div ref={ref} {...props}>
      {ownerState?.conversation?.title ?? 'No conversation selected'}
      {memberCount != null && (
        <span style={{ fontWeight: 400, marginLeft: 8 }}>{memberCount} members</span>
      )}
    </div>
  );
});

function CustomConversationTitle(props) {
  return <ChatConversationTitle {...props} slots={{ root: LiveTitle }} />;
}

Message list anatomy

ChatMessageList renders three styled slots: a flex column outer shell, a scrolling scroller, and a padded content container.

import { ChatMessageList, ChatMessageGroup } from '@mui/x-chat';

ownerState and how state flows

Slot key Default element ownerState fields
messageList div messageCount, isAtBottom
messageListScroller div same
messageListContent div same

The isAtBottom flag is useful for toggling a scroll-to-bottom affordance. messageCount lets you show an empty-state illustration when the list has no messages.

Overriding the content container

Use slotProps when you only need to pass additional styling without swapping the element type:

<ChatMessageList
  renderItem={({ id, index }) => <ChatMessageGroup index={index} messageId={id} />}
  slotProps={{
    messageListContent: {
      sx: { gap: 1, paddingBlock: 2 },
    },
  }}
/>

Message groups and individual messages

ChatMessageGroup decides whether consecutive messages from the same author form a visual group, exposing isFirst and isLast grouping flags. ChatMessage then uses those flags to adjust spacing and avatar visibility.

import {
  ChatMessageGroup,
  ChatMessage,
  ChatMessageAvatar,
  ChatMessageContent,
  ChatMessageMeta,
  ChatMessageActions,
} from '@mui/x-chat';

ChatMessageGroup ownerState

Field Type Description
isFirst boolean First message in a consecutive group
isLast boolean Last message in a consecutive group
authorRole 'user' | 'assistant' Role of the group author
authorId string | undefined Identity of the group author

ChatMessage ownerState

Field Type Description
role 'user' | 'assistant' Author role, drives bubble alignment
status string Delivery/streaming status
streaming boolean Whether the message is still streaming
error boolean Whether the message ended in an error state
isGrouped boolean Whether this row is part of a group
showAvatar boolean Controls the phantom-column width calculation

Overriding the message bubble

The recommended pattern for customizing the bubble is to wrap ChatMessageContent and replace only its inner bubble slot. This preserves part iteration, markdown rendering, tool-call renderers, and source citations:

MUI Assistant
Material UI chat
Styled with your active MUI theme
Material UI chat

Styled with your active MUI theme

MUI Assistant
MUI Assistant

Hello! I am styled using your active Material UI theme. Try sending a message.

You
You

Great — the bubble colors come from palette.primary and the typography from the theme.

Forward ref and spread ...props before your overrides so the wrapping pattern does not silently discard required props.

Adding custom children to a message row

When you need to insert additional content inside a message row — for example a copy-to-clipboard button — provide custom children to ChatMessage:

<ChatMessage messageId={id}>
  <ChatMessageAvatar />
  <ChatMessageContent />
  <CopyButton messageId={id} />
  <ChatMessageMeta />
</ChatMessage>

Children passed to ChatMessage are rendered inside the grid layout, so they inherit the same column structure as the default slots.

Composer anatomy

ChatComposer is a <form> element that handles text input, attachment state, and submission. Its children own specific regions of the composer.

import {
  ChatComposer,
  ChatComposerTextArea,
  ChatComposerToolbar,
  ChatComposerAttachButton,
  ChatComposerSendButton,
  ChatComposerHelperText,
} from '@mui/x-chat';

ownerState and how state flows

Every composer component shares the same ownerState shape:

Field Type Description
isSubmitting boolean Form submission in progress
hasValue boolean Textarea has non-empty value
isStreaming boolean An assistant response is streaming
attachmentCount number Number of staged attachments
disabled boolean Input is disabled

ChatComposerHelperText also receives an error boolean.

Changing the placeholder and disabling the attach button

Use slotProps on ChatBox for light prop-only overrides that do not require a component replacement:

<ChatBox
  slotProps={{
    composerInput: {
      placeholder: 'Ask a question…',
      'aria-label': 'Message input',
    },
    composerAttachButton: {
      sx: { display: 'none' },
    },
  }}
/>

Overriding the send button with an ownerState-aware component

The isStreaming flag lets you replace the send icon with a stop icon while a response is in progress:

MUI Assistant
Material UI chat
Styled with your active MUI theme
Material UI chat

Styled with your active MUI theme

MUI Assistant
MUI Assistant

Hello! I am styled using your active Material UI theme. Try sending a message.

You
You

Great — the bubble colors come from palette.primary and the typography from the theme.

Accessing message state in custom children

Custom children placed inside ChatMessage receive the message's ownerState through the slot system. When you need to read message state (role, streaming status, error) inside a deeply nested component, define a custom slot and accept ownerState as a prop:

MUI Assistant
Material UI chat
Styled with your active MUI theme
Material UI chat

Styled with your active MUI theme

MUI Assistant
MUI Assistant

Hello! I am styled using your active Material UI theme. Try sending a message.

You
You

Great — the bubble colors come from palette.primary and the typography from the theme.

The ownerState includes role, status, streaming, error, isGrouped, and showAvatar — the same fields listed in the ChatMessage ownerState table above.

For conversation-level state (the active conversation title, participants, etc.), use the ownerState received by header slot components — see the Header anatomy section above.

Full recomposition example

When ChatBox slots are not enough — for example when you want to add a pinned banner between the header and the message list, or position the typing indicator inside the header instead of above the composer — you can assemble the thread from individual Material UI components directly.

The following example shows a fully assembled thread pane without relying on ChatBox layout defaults. It inserts a custom warning banner between the header and the message list:

Material UI chat

Styled with your active MUI theme

Responses are AI-generated. Verify before acting.
MUI Assistant
MUI Assistant

Hello! I am styled using your active Material UI theme. Try sending a message.

You
You

Great — the bubble colors come from palette.primary and the typography from the theme.

Wrap your custom thread with a ChatProvider from @mui/x-chat to wire runtime state to your adapter.

See also

API

API

See the documentation below for a complete reference to all of the props and classes available to the components mentioned here.