Components
ConversationList
A named list of conversations, such as the column beside a chat: each row links to one, with an avatar, the name, the time and the last message, and a count of unread messages. The open conversation takes the muted accent fill.
Preview
import { Avatar } from "@/components/ui/avatar"
import { ConversationList, ConversationListItem } from "@/components/ui/conversation-list"
import { GroupAvatar } from "@/components/ui/group-avatar"
const designTeam = [{ name: "Linh Tran" }, { name: "An Pham" }, { name: "Minh Le" }, { name: "Bao Do" }]
<ConversationList label="Conversations" className="w-80 max-w-full">
<ConversationListItem active href="#linh" title="Linh Tran" preview="Got it, thanks!" time="10:44" avatar={<Avatar alt="" color="blue" size="lg">LT</Avatar>} />
<ConversationListItem href="#design" title="Design team" preview="Minh: The new icons are in." time="09:12" unread={3} avatar={<GroupAvatar alt="" size="lg" members={designTeam} />} />
<ConversationListItem href="#an" title="An Pham" preview="See you Monday." time="Mon" avatar={<Avatar alt="" color="green" size="lg">AP</Avatar>} />
</ConversationList>Usage
When to use
- List the conversations beside a chat, with the last message, the time and unread counts.
- Switch between past chats with an assistant.
When not to use
- The main navigation of an app. Use Sidebar.
- A list of people to pick from. Use Combobox.
- Rows of data with columns. Use Table.
Examples
Inbox
import { Avatar } from "@/components/ui/avatar"
import { ConversationList, ConversationListItem } from "@/components/ui/conversation-list"
import { GroupAvatar } from "@/components/ui/group-avatar"
const designTeam = [{ name: "Linh Tran" }, { name: "An Pham" }, { name: "Minh Le" }, { name: "Bao Do" }]
<ConversationList label="Conversations" className="w-80 max-w-full">
<ConversationListItem active href="#linh" title="Linh Tran" preview="Got it, thanks!" time="10:44" avatar={<Avatar alt="" color="blue" size="lg">LT</Avatar>} />
<ConversationListItem href="#design" title="Design team" preview="Minh: The new icons are in." typing="Minh is typing" time="09:12" unread={3} avatar={<GroupAvatar alt="" size="lg" members={designTeam} />} />
<ConversationListItem href="#an" title="An Pham" preview="See you Monday." time="Mon" avatar={<Avatar alt="" color="green" size="lg">AP</Avatar>} />
</ConversationList>Without avatars
Rows without avatars suit a list of threads or assistant chats. unread as true shows a dot.
import { ConversationList, ConversationListItem } from "@/components/ui/conversation-list"
<ConversationList label="Chats" className="w-72 max-w-full">
<ConversationListItem active title="Quarterly report summary" preview="Sales grew in two of three regions." time="Today" />
<ConversationListItem unread title="Onboarding email draft" preview="Here is a shorter version." time="Yesterday" />
<ConversationListItem title="SQL for churn by plan" preview="The query groups accounts by plan." time="Oct 20" />
</ConversationList>Do and don't
Do
- Keep the list in its own column beside the MessageList, or in a Sheet on a phone.
- Show the last message and its time, so the reader can pick a conversation without opening it.
- Mark the open conversation with active.
Don't
- Do not use for site or app navigation; use Sidebar.
- Do not show a count of 0; leave unread out once the messages are read.
- Do not put buttons inside a row; the whole row is one link.
Props
| Name | Type | Values | Default | Description |
|---|---|---|---|---|
| label required | string | - | - | On ConversationList: names the navigation landmark, such as Conversations. |
| title required | node | - | - | On ConversationListItem: the person, group or agent, such as Linh Tran. |
| preview | node | - | - | The last message, on one line. |
| time | node | - | - | When the last message came, as shown, such as 10:42 or Mon. |
| dateTime | string | - | - | The machine-readable time, such as 2026-10-23T10:42. |
| avatar | node | - | - | An Avatar at the start, or a GroupAvatar for a group. Pass alt="" when title names the same person or group. |
| unread | number | boolean | - | - | The number of unread messages, or true for an unread dot. 0 or false: read. |
| typing | boolean | string | - | false | Someone is typing in this conversation: the last message gives way to pulsing dots and the words, in secondary text. A string replaces the words, such as Linh is typing. |
| active | boolean | - | false | The open conversation: aria-current="page" and the muted accent fill. |
| href | string | - | - | With it the row is a link; without it a button. |
| asChild | boolean | - | false | For router links: the row's classes and content move onto its only child. |
| labels | { unread?: (count: number | undefined) => string; typing?: string } | - | - | On ConversationList: the words read after an unread row (count is undefined for a dot) and shown while someone types. Defaults: 3 unread messages, Unread, Typing. Vietnamese: 3 tin chưa đọc, Chưa đọc, Đang soạn tin. |
Anatomy
| Part | Description |
|---|---|
| root | The navigation landmark. |
| list | The ul of rows. |
| conversation | One row: a link or button with the avatar, then two lines. |
| avatar | Optional Avatar, or GroupAvatar for a group, at the start. Size lg suits two lines. |
| title | The person, group or agent, on one line. Semibold while unread. |
| time | When the last message came, at the end of the first line. |
| preview | The last message on one line, cut with an ellipsis. Primary text while unread. |
| typing | While someone types, in place of the last message: three 4px dots that pulse one after the other on duration-loop (still under reduced motion) and the words, in secondary text. |
| unread | The unread count: a solid accent pill (99+ above 99), or an 8px accent dot for unread=true. |
Accessibility
Role: navigation
| Keys | Action |
|---|---|
| Tab | Moves to the next conversation. |
| Enter | Opens the focused conversation. |
- A navigation landmark named by label (EN `Conversations`, VI `Cuộc trò chuyện`), with the rows in a list.
- Each row is one link (or a button without href), so its name is everything it shows: name, time and last message.
- The open conversation has aria-current="page".
- The unread count and dot are hidden from screen readers; the row ends with words from labels.unread (EN `3 unread messages`, `Unread`). An unread row is also told apart by weight and text color, not by the accent pill alone.
- Pass alt="" to the Avatar when title names the same person.
- While someone types, the row's name ends with the typing words instead of the last message; the dots are hidden from screen readers.
Install
npx werewolf-ui add conversation-listRun npx werewolf-ui init once in your app before adding components. Init sets up tokens, their CSS import and agent rules. Add installs this component and its dependencies while preserving your theme. See Installation for requirements and the alternative registry setup.