Chat - Tailwind CSS
Style chat components using Tailwind CSS utility classes with the structural primitives from @mui/x-chat.
The chat primitives ship structural components with zero built-in styles.
Each component renders semantic HTML elements with data-* attributes that reflect component state, making them a natural fit for Tailwind CSS utility classes.
Installation
Install the package alongside Tailwind:
npm install @mui/x-chatNamespace API
Structural primitives are available as namespace exports for clean dot-notation usage:
import {
Chat,
Composer,
Message,
MessageList,
Conversation,
ConversationList,
Indicators,
} from '@mui/x-chat/headless';
// Usage
<Chat.Root>
<Chat.Layout>
<MessageList.Root renderItem={...} />
<Composer.Root>
<Composer.TextArea />
<Composer.Toolbar>
<Composer.AttachButton />
<Composer.SendButton />
</Composer.Toolbar>
</Composer.Root>
</Chat.Layout>
</Chat.Root>
Applying Tailwind classes
Pass className to any structural primitive:
<Composer.Root className="flex flex-col gap-1 border-t border-gray-200 p-3 bg-white">
<Composer.TextArea
className="resize-none rounded-lg border border-gray-300 px-3 py-2 text-sm
focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<Composer.Toolbar className="flex items-center justify-end gap-2">
<Composer.AttachButton className="rounded p-1.5 text-gray-500 hover:bg-gray-100" />
<Composer.SendButton
className="rounded-lg bg-blue-600 px-3 py-1.5 text-sm font-medium text-white
hover:bg-blue-700 disabled:opacity-40"
/>
</Composer.Toolbar>
</Composer.Root>
Data attributes
Every structural primitive exposes data-* attributes that reflect its current state.
Use Tailwind's attribute selectors to style state variations without JavaScript:
{
/* The send button sets data-disabled when the composer has the disabled prop */
}
<Composer.SendButton className="bg-blue-600 text-white data-[disabled]:opacity-40 data-[disabled]:cursor-not-allowed" />;
{
/* Message root sets data-role="user" or data-role="assistant" */
}
<Message.Root className="flex gap-3 data-[role=user]:flex-row-reverse" />;
{
/* Typing indicator sets data-count when users are typing; returns null when count=0 */
}
<Indicators.TypingIndicator className="flex items-center gap-1 text-sm text-gray-500" />;
Common data attributes
| Component | Attribute | Values | Description |
|---|---|---|---|
Message.Root |
data-role |
"user", "assistant" |
Message sender role |
Message.Root |
data-status |
"pending", "sending", "streaming", "sent", "read", "error", "cancelled" |
Message delivery status |
Composer.SendButton |
data-disabled |
present / absent | Composer has the disabled prop |
Composer.Root |
data-is-streaming |
present / absent | Response in progress |
ConversationList.Item |
data-selected |
present / absent | Currently selected conversation |
Slot props for custom elements
Use the slots and slotProps props to replace inner elements while keeping behavior intact:
<MessageList.Root
slots={{
messageListContent: 'div',
}}
slotProps={{
messageListContent: {
className: 'flex flex-col gap-2 p-4',
},
}}
renderItem={({ id }) => <MyMessage key={id} id={id} />}
/>
See also
- Styling for Material UI theme-based customization.
- Slots & Composition for replacing subcomponents entirely.
API
See the documentation below for a complete reference to all of the props and classes available to the components mentioned here.