Skip to content
ShellThread listMenu

Thread list

The rows and groups the sidebar is built from: a roving-focus list, date grouping, inline rename, a row menu and a status mark for running, waiting, failed and unread threads.

Every row state: running, needs input, failed, unread, a long title and an untitled chat.

Installation

$ pnpm dlx shadcn@latest add https://chatcn.dev/r/thread-list.json

Usage

tsx
import {
  groupThreadsByDate,
  ThreadList,
  ThreadListGroup,
  ThreadListItem,
} from "@/components/ui/chatcn/thread-list"

<ThreadList>
  {groupThreadsByDate(threads).map((group) => (
    <ThreadListGroup key={group.id} label={group.label}>
      {group.threads.map((thread) => (
        <ThreadListItem
          key={thread.id}
          threadId={thread.id}
          title={thread.title}
          status={thread.status}
          active={thread.id === activeId}
          onSelect={() => open(thread.id)}
          onRename={(title) => rename(thread.id, title)}
          onDelete={() => remove(thread.id)}
        />
      ))}
    </ThreadListGroup>
  ))}
</ThreadList>

Use these when your sidebar needs a different shape, for example agent runs grouped by repository. groupThreadsByDate is a plain function; ThreadListSkeleton matches the row layout for loading.

Status

StatusMarkSpoken
streamingSpinnerGenerating
needs-inputHand, amberNeeds your input
failedAlert circle, redFailed
unreadSmall ink dotUnread
idleNothing

Props

ThreadListItem

PropTypeNotes
threadIdstringUsed for focus tracking
titlestringEmpty shows placeholder (New chat)
statusThreadStatusSee above
activebooleanaria-current="page"
hrefstringRenders a link instead of a button
onSelect() => void
highlightMatchRange[]Emphasize filter matches in the title
onRename, onDelete, onPinChange, onShareEach adds its menu item
menuItemsReactNodeExtra MenuItems
renaming, onRenamingChangeControlled inline rename

groupThreadsByDate

OptionTypeNotes
nowDate | numberReference time. Defaults to now
pinnedFirstbooleanDefault true
localestringMonth names
labelsobjectRename Pinned, Today, Yesterday and the rest

Keyboard

KeyAction
↑, ↓, Home, EndMove between rows
EnterOpen the thread
F2 or double-clickRename in place. Enter saves, Esc cancels
DeleteDelete
Shift+F10, ContextMenuOpen the row menu

Accessibility

  • Rename never saves while an input method is composing, so Japanese, Chinese and Korean titles work.
  • On touch screens the menu button is always visible at 44 px, with the status mark beside it.

Motion

  • Titles fade out at the end instead of truncating with an ellipsis, mirrored in RTL.
  • Keyboard moves between rows are instant and scroll the row into view without smoothing.