Skip to content
+

Chat - Tailwind CSS

Style the Chat with Tailwind CSS utility classes on the headless primitives from @mui/x-chat/headless.

@mui/x-chat/headless ships structural Chat primitives 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 @mui/x-chat alongside Tailwind CSS:

npm install @mui/x-chat

Using namespace imports

Structural primitives are exported as namespaces so you can compose them with dot notation:

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 primitive component:

<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 primitive component 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 is empty or streaming */
}
<Composer.SendButton className="bg-blue-600 text-white data-[disabled]:opacity-40 data-[disabled]:cursor-not-allowed" />;

{
  /* Message root sets data-author="self" or data-author="other" */
}
<Message.Root className="flex gap-3 data-[author=self]:flex-row-reverse" />;

{
  /* Typing indicator sets data-visible when the assistant is responding */
}
<Indicators.TypingIndicator className="hidden data-[visible]:flex items-center gap-1 text-sm text-gray-500" />;

Common data attributes

Component Attribute Values Description
Message.Root data-author "self", "other" Current user vs. other participants
Message.Root data-status "sending", "sent", "error" Message delivery status
Composer.SendButton data-disabled present / absent Empty input or streaming
Composer.Root data-streaming present / absent Response in progress
ConversationList.Item data-active present / absent Currently selected conversation

Replacing inner elements with slot props

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} />}
/>

Complete demo: Chat shell

The demo below shows a Chat styled entirely with Tailwind CSS utility classes on the structural primitives from @mui/x-chat/headless: