Chat - Conversation Header
Display the active conversation's title, subtitle, participants, and action buttons using the themed header components.
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';
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)
The header sits at the top of the thread pane and provides the visual identity of the active conversation.
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.
Title and subtitle
ChatConversationTitle renders the conversation name from conversation.title. ChatConversationSubtitle renders the secondary line from conversation.subtitle, which can include participant names, a presence indicator, or any descriptive text.
The ChatConversation type provides these fields:
interface ChatConversation {
id: string;
title?: string;
subtitle?: string;
avatarUrl?: string;
participants?: ChatUser[];
unreadCount?: number;
readState?: ConversationReadState;
lastMessageAt?: ChatDateTimeString;
metadata?: ChatConversationMetadata;
}
Participants
The participants array on ChatConversation carries ChatUser objects with displayName, avatarUrl, and isOnline fields. Use these in a custom subtitle slot to show a participant list or online status:
const ParticipantSubtitle = React.forwardRef(function ParticipantSubtitle(
{ ownerState, ...props },
ref,
) {
const participants = ownerState?.conversation?.participants ?? [];
const onlineCount = participants.filter((p) => p.isOnline).length;
return (
<span ref={ref} {...props}>
{participants.length} participants ({onlineCount} online)
</span>
);
});
Action buttons
ChatConversationHeaderActions renders an action area on the right side of the header. Replace it to add archive, mute, or context menu buttons:
import IconButton from '@mui/material/IconButton';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import ArchiveIcon from '@mui/icons-material/Archive';
const CustomActions = React.forwardRef(function CustomActions(
{ ownerState, ...props },
ref,
) {
if (!ownerState?.hasConversation) return null;
return (
<div ref={ref} {...props}>
<IconButton size="small" aria-label="Archive conversation">
<ArchiveIcon fontSize="small" />
</IconButton>
<IconButton size="small" aria-label="More options">
<MoreVertIcon fontSize="small" />
</IconButton>
</div>
);
});
Overriding a header slot
The most targeted customization is to replace the element type on one slot while keeping everything else:
import { ChatConversationHeader } from '@mui/x-chat';
import { styled } from '@mui/material/styles';
const GradientHeader = styled('header')(({ theme }) => ({
background: `linear-gradient(135deg, ${theme.palette.primary.dark}, ${theme.palette.primary.main})`,
color: theme.palette.primary.contrastText,
'& *': { color: 'inherit' },
}));
function CustomHeader(props) {
return <ChatConversationHeader {...props} slots={{ header: GradientHeader }} />;
}
Pass CustomHeader through ChatBox's slots.conversationHeader prop:
<ChatBox slots={{ conversationHeader: CustomHeader }} />
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 }} />;
}
Full recomposition
When you need to insert additional content inside the header — for example a typing indicator or a custom divider — assemble the header from individual Material UI components directly:
import {
ChatConversation,
ChatConversationHeader,
ChatConversationTitle,
ChatConversationSubtitle,
ChatConversationHeaderActions,
} from '@mui/x-chat';
import Box from '@mui/material/Box';
function CustomThread() {
return (
<ChatConversation sx={{ height: '100%' }}>
<ChatConversationHeader>
<ChatConversationTitle />
<ChatConversationSubtitle />
<Box sx={{ flex: 1 }} />
<ChatConversationHeaderActions />
</ChatConversationHeader>
{/* rest of the thread */}
</ChatConversation>
);
}
See also
- Conversation List for the sidebar that lists conversations.
- Real-Time Sync for live updates to conversation metadata displayed in the header.
- Layout for the full thread anatomy including message list and composer.
API
See the documentation below for a complete reference to all of the props and classes available to the components mentioned here.