Sidebar
Application sidebar with workspace switcher, navigation, and user menu.
The dashboard sidebar is the primary navigation element, providing workspace switching, feature navigation, and user account access.
Structure
┌─────────────────────────┐
│ ◆ Workspace Name ▼ │ ← WorkspaceSwitcherV2
│ Pro Plan │
├─────────────────────────┤
│ │
│ ▸ Workspace │ ← NavMain (collapsible)
│ Home │
│ Details │
│ Members │
│ ▸ Chats │
│ New Chat │
│ History │
│ ▸ Knowledge Bases │
│ All KBs │
│ ▸ Documents │
│ All Documents │
│ │
├─────────────────────────┤
│ Dashboard │ ← NavOther (links)
│ Workspaces │
│ Settings │
│ Billing │
├─────────────────────────┤
│ 👤 user@example.com ▲ │ ← NavUser (dropdown)
└─────────────────────────┘AppSidebar Component
The main sidebar wrapper receives workspace and user context from the layout:
interface AppSidebarProps {
workspaces: Workspace[];
user: UserDetails;
currentWorkspaceId?: string;
}It builds navigation items dynamically based on currentWorkspaceId:
const navMain = [
{
title: "Workspace",
icon: Building2,
items: [
{ title: "Home", url: `/dashboard/workspaces/${id}` },
{ title: "Details", url: `/dashboard/workspaces/${id}/details` },
],
},
{
title: "Chats",
icon: MessageSquare,
items: [
{ title: "New Chat", url: `/dashboard/workspaces/${id}/chat` },
{ title: "History", url: `/dashboard/workspaces/${id}/chat/history` },
],
},
// ...
];If no workspace is selected, navigation items are disabled.
Workspace Switcher
WorkspaceSwitcherV2 provides a dropdown to switch between workspaces:
- Lists all user workspaces with icons
- Shows current subscription status badge
- Includes a "Create Workspace" button that opens a dialog
- Navigates via
router.push()on selection
function WorkspaceSwitcherV2({ workspaces, currentWorkspaceId }) {
const router = useRouter();
const handleSelect = (workspace: Workspace) => {
router.push(`/dashboard/workspaces/${workspace.id}`);
};
// ...
}NavMain (Collapsible Groups)
Primary navigation with collapsible sections:
interface NavMainProps {
items: {
title: string;
url: string;
icon: LucideIcon;
isActive?: boolean;
items: { title: string; url: string }[];
}[];
}Each group expands/collapses with an animated chevron. Active state is determined by matching usePathname() against the item URLs.
NavUser (Account Menu)
User dropdown in the sidebar footer:
┌─────────────────────────┐
│ 👤 John Doe │
│ john@example.com │
├─────────────────────────┤
│ Profile │
│ Billing │
│ Workspaces │
│ Settings │
├─────────────────────────┤
│ Sign Out │
└─────────────────────────┘Sign Out calls the signOutAction() server action.
Mobile Sidebar
On mobile (< 768px), the sidebar renders as a Sheet (slide-out panel) instead of a persistent sidebar:
const isMobile = useIsMobile();
{isMobile ? (
<Sheet>
<SheetTrigger asChild>
<Button variant="ghost" size="icon"><Menu /></Button>
</SheetTrigger>
<SheetContent side="left">
<SidebarContent />
</SheetContent>
</Sheet>
) : (
<aside className="w-64">
<SidebarContent />
</aside>
)}Sidebar Provider
The SidebarProvider context manages sidebar state (open/closed, mobile vs desktop):
<SidebarProvider>
<AppSidebar {...props} />
<SidebarInset>
<header>
<SidebarTrigger />
<DynamicBreadcrumb />
</header>
<main>{children}</main>
</SidebarInset>
</SidebarProvider>The SidebarTrigger button toggles the sidebar on desktop (collapse) and mobile (sheet).
Dynamic Breadcrumb
The DynamicBreadcrumb component auto-generates breadcrumbs from the current route path:
Dashboard > Workspaces > My Workspace > Chats > Research ChatEach segment is a clickable link except the last (current page).