Skip to content
ShellChat sidebarMenu

Chat sidebar

The thread sidebar: New chat, a filter, threads grouped by date with Pinned on top, inline rename, delete with undo, live status per thread, a 52 px rail and a resizable edge.

Threads grouped by date with pinned on top, rename, delete with undo, filter, resize and the rail.

Installation

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

Usage

tsx
import { ChatSidebar, ChatSidebarAccount } from "@/components/ui/chatcn/chat-sidebar"

<ChatSidebar
  threads={threads}
  activeId={threadId}
  onSelect={(id) => router.push(`/c/${id}`)}
  onNewChat={newChat}
  onRename={(id, title) => renameThread(id, title)}
  onPinChange={(id, pinned) => pinThread(id, pinned)}
  onDelete={(id) => deleteThread(id)}
  brand="Tallyline"
  footer={<ChatSidebarAccount name="Leo Marchetti" detail="Pro plan" />}
/>

A thread is { id, title, updatedAt, pinned?, status? }. The sidebar sorts by updatedAt and groups into Pinned, Today, Yesterday, Previous 7 days, Previous 30 days, then one group per month. Pass grouping={{ now }} for stable demos and tests.

Deleting is optimistic: the row leaves at once and an Undo toast appears at the bottom of the sidebar. onDelete runs when the undo window ends (undoMs, 5 s by default, paused while the toast is hovered or focused). Cmd/Ctrl+Z undoes too. Set undoMs={0} to delete immediately.

Inside a ChatLayout the layout owns the collapsed state and Cmd/Ctrl+B, and on mobile the sidebar becomes the drawer content: no rail, no resize handle, and picking a thread closes the drawer.

Examples

States

Loading shows skeleton rows that match the real list. A failed load says how to fix it and offers Retry. The rail keeps New chat and Search one click away, with tooltips and their shortcuts.

Loading, empty, failed to load and the collapsed rail.

Props

PropTypeNotes
threadsChatThread[]Sorted and grouped for you
activeIdstring | nullSets aria-current on the row
onSelect(id) => voidOr getHref to render rows as links
onNewChat() => voidShows New chat and binds Cmd/Ctrl+Shift+O
onRename(id, title) => voidEnables Rename, double-click and F2
onPinChange(id, pinned) => voidShows Pin or Unpin
onDelete(id) => voidCalled once the undo window ends
onShare(id) => voidShows Share in the row menu
renderMenuItems(thread) => ReactNodeExtra MenuItems before Delete
undoMsnumberDefault 5000. 0 deletes at once
loading, error, onRetrySkeleton and failure states
brand, header, footerReactNodeTop row, under the filter, bottom
railHeader, railFooterReactNodeIcon-only versions for the rail
search, searchPlaceholderThe filter field. On by default
onSearchClick() => voidRail Search opens your chat search instead
collapsed, defaultCollapsed, onCollapsedChangeRail mode
width, defaultWidth, onWidthChangenumberDefault 260
minWidth, maxWidthnumberDefault 240 and 288
resizablebooleanDrag handle on the inline-end edge
groupingGroupThreadsOptionsnow, locale, group labels
hotkeys, newChatHotkey, sidebarHotkeyDefault mod+shift+o and mod+b

Keyboard

KeyAction
Cmd/Ctrl+Shift+ONew chat
Cmd/Ctrl+BCollapse or expand (inside ChatLayout, or standalone)
↓ in the filterMove to the first result
↑, ↓, Home, EndMove between threads
F2Rename the focused thread
DeleteDelete the focused thread
Shift+F10Open the row menu
Cmd/Ctrl+ZUndo the last delete while the toast shows
←, → on the edgeResize by 8 px, 24 px with Shift. Enter collapses

Accessibility

  • An aside labelled Sidebar holds a nav labelled Chats. Group labels are headings, rows sit in lists.
  • One row is in the tab order at a time (roving focus), so Tab leaves the list in one step. Tab from a row reaches its menu button.
  • Status marks are never color alone: a spinner, a hand, an alert circle or a dot, each with a spoken label.
  • The resize edge is a separator with aria-valuenow in px. The Undo toast is a polite status region.
  • Deleting the focused thread moves focus to its neighbour.

Motion

  • Row hover fills in 150 ms. The row menu button appears instantly, since rows are hovered all day.
  • A generated title replacing New chat fades in once over 200 ms.
  • The Undo toast rises over 400 ms on the plain ease. Collapsing with the pointer fades the content in over 150 ms; from the keyboard it is instant. Width never animates.