Sidebar Nav
Navigate a chat product with primary destinations, a searchable list of past conversations, and a collapse to an icon rail.
Overview
Sidebar Nav is the side navigation of a chat product. It has a short list of destinations, a New chat button, and the person's past conversations with a search field over them. It collapses to a rail of icons.
It renders the items and threads you pass and reports what was pressed. Routing and loading conversations are your application's work.
Anatomy
- HeaderYour workspace name or switcher, and the collapse button
- DestinationsNew chat, then one row for each item
- ChatsA search field and the list of past conversations
- FooterYour own content, such as an account menu
Installing sidebar-nav brings Button along as a registry dependency.
Behavior
Collapsing
Collapsed, the nav keeps the collapse button and the destination icons. The header, the chat list, and the footer are left out, because the rail has no room for their text. Control it with collapsed and onCollapsedChange, or let it manage itself.
Searching chats
Typing filters the chats by title. When nothing matches, the list is replaced by a message that names the search. With no chats at all, the search field is left out and the list says so.
Accessibility
The component is a navigation landmark with a name. Every row is a button. The current destination is marked with aria-current="page", and the open conversation with aria-current.
The collapse button reports whether the nav is expanded. Collapsed, each destination keeps its label as its accessible name. The empty and no-match messages are statuses. With reduced motion, the width changes at once.
Installation
Usage
import { SidebarNav } from "@/components/ai/sidebar-nav"
<SidebarNav
header={<WorkspaceName />}
items={[
{ id: "home", label: "Home", icon: <HomeIcon /> },
{ id: "files", label: "Files", icon: <FileIcon /> },
]}
activeItemId={route.section}
onNavigate={(itemId) => router.push(`/${itemId}`)}
threads={chats}
activeThreadId={route.chatId}
onThreadSelect={(threadId) => router.push(`/chat/${threadId}`)}
onNewChat={() => router.push("/chat/new")}
/>The nav fills its parent's height, so place it in a full-height layout.
Examples
Collapsed, with no chats
The icon rail. Expand it to see the empty chat list.
API reference
SidebarNav
Accepts native nav props beyond those listed.