ConversationList
StablePast chats grouped by day, with a New chat button, for the side bar.
Also known as chat history, conversation history, threads list, sidebar history
Install with npm install @tattva/ui (setup), or copy the code from the kit in Downloads.
Grouped history
const [active, setActive] = useState("2");
<div className="w-65">
<ConversationList
activeId={active}
onSelect={setActive}
onNew={() => {}}
items={[
{ id: "1", title: "Q3 revenue analysis", group: "Today" },
{ id: "2", title: "Onboarding email draft", group: "Today" },
{ id: "3", title: "SQL help", group: "Yesterday" },
{ id: "4", title: "Meeting notes summary", group: "Yesterday" },
]}
/>
</div>Each chat has a group name. Groups show in the order they first appear.
When to use it
The history of past chats for the side panel. A New chat button sits on top, so starting fresh is always one press away.
Made of: New chat button · Group name · Chat title
Use it for
- Past chats in the AppShell side panel, grouped by day.
- Switching between chats, with the open one highlighted.
- Starting a new chat from the side panel.
Not for
- The main site menu or links to other parts of the app.
- The messages inside one chat: use MessageList.
Do and avoid
Do
- Use groups such as Today and Yesterday, newest first.
- Keep titles short. Long ones wrap onto a second line and make the list harder to scan.
- Place it in the side bar of AppShell, which sets the width.
- Keep the highlight matching the chat that is open.
Avoid
- Do not use it as the main site menu. It lists chats only.
- Do not expect it to sort. It keeps the order you give it.
- Do not add extra actions inside a row. Each row is one button.
On a phone
- Each row title is a full-width button with a tap area at least 44px tall on a touch screen, and long titles wrap instead of being cut off.
- The row menu button grows from 32px to 44px on a touch screen, and the menu opens as a bottom sheet.
- Delete asks in place with Keep and Delete side by side, each with a 44px tap area.
Properties
| Prop | Type | Default | Description |
|---|---|---|---|
| itemsrequired | ConversationItem[] | none | Conversations to show. ConversationItem is { id: string; title: string; group?: string }. |
| activeId | string | none | Id of the open conversation. It receives aria-current and the active style. |
| onSelectrequired | (id: string) => void | none | Called with the id when an item is clicked. |
| onNewrequired | () => void | none | Called when New chat is pressed. |
| onRename | (id: string) => void | none | When given, each row gets a More menu with Rename. Your code asks for the new name. |
| onDelete | (id: string) => void | none | When given, each row gets Delete in its More menu. Pressing it asks once, in place, with Keep it as easy to press as Delete and focus on Keep it. Called only after the person confirms. |
| labels | { delete?: string; keep?: string; confirm?: (title: string) => string } | none | The words of the in-place confirmation. |
"Show code under the preview gives the code for the example on the stage."
States
States this part has beyond its default look. Build each one your screen can reach. States set by a property are in the Code tab.
- current
- The item whose id matches activeId gets a highlighted background and aria-current set to page.
- hover
- An item gets a soft background under the pointer.
- focus
- A visible focus ring appears on an item or the New chat button on keyboard focus.
- empty
- With no items, only the New chat button is shown.
- row menu
- With onRename or onDelete, each row gets a More menu (drawn on the page, so a scrolling side bar cannot clip it).
- confirming delete
- Choosing Delete swaps the row for "Delete this chat?" with Keep it and Delete side by side; focus lands on Keep it and Escape cancels.
Tokens
Change one of these and every part that uses it follows.
--surface-hover (bg-hover)--fg-muted--fg-subtle--radius-control--radius-card
Built in
- It is a navigation area named "Conversations".
- Each chat is a real button, so Tab, Enter and Space work.
- The open chat is marked as current for screen readers and gets a background, so color is not the only clue.
- Each group of chats is its own list, so screen readers say how many chats it holds.
What you need to do
- Keep activeId in step with the chat that is open, so the right one is announced as current.
- Write titles that tell chats apart. The title is all a screen reader hears for each row.
- Use one per page. Its navigation name, "Conversations", is fixed.
Built toward WCAG 2.2 AA and checked automatically; no independent audit yet. Read the accessibility guide