VayuUI

Sidebar

A composable layout component for navigation screens, with responsive behavior and mobile menu support.

Installation

npx vayu-ui-cli@latest add sidebar

Usage

Dashboard

<SidebarProvider>
  <Sidebar>
    <SidebarHeader>
      <h2>Acme Inc</h2>
      <MobileMenuButton />
    </SidebarHeader>
    <SidebarContent>
      <SidebarMenu>
        <SidebarMenuGroup label="Application">
          <SidebarMenuItem href="/dashboard" active badge="3">
            Dashboard
          </SidebarMenuItem>
          <SidebarMenuItem href="/analytics">Analytics</SidebarMenuItem>
        </SidebarMenuGroup>
      </SidebarMenu>
    </SidebarContent>
    <SidebarFooter>
      <SidebarToggle floating />
    </SidebarFooter>
  </Sidebar>
</SidebarProvider>

Anatomy

<SidebarProvider>
  <Sidebar>
    <SidebarHeader>
      <MobileMenuButton />
    </SidebarHeader>
    <SidebarContent>
      <SidebarMenu>
        <SidebarMenuGroup label="...">
          <SidebarMenuItem icon={<Icon />} badge="..." href="...">
            Item Name
          </SidebarMenuItem>
        </SidebarMenuGroup>
      </SidebarMenu>
    </SidebarContent>
    <SidebarFooter>
      <SidebarToggle floating />
    </SidebarFooter>
  </Sidebar>
</SidebarProvider>

Dashboard Shell Layout

The sidebar must live inside a locked-height shell so its internal scrolling works correctly.

<div className="h-screen overflow-hidden flex bg-canvas">
  <SidebarProvider>
    <MobileMenuButton />
    <Sidebar>...</Sidebar>
    <main className="flex-1 overflow-y-auto p-6">...</main>
  </SidebarProvider>
</div>

Why this matters: Sidebar uses h-screen and SidebarContent uses overflow-y-auto. If the outer wrapper stretches with content (min-h-screen without overflow-hidden), the sidebar grows with the page and the internal scrollbar never engages — the entire page scrolls instead.

SidebarHeader and SidebarFooter accept a function as children so you can render different content when the sidebar is collapsed to its 80px rail:

<SidebarHeader>
  {({ collapsed, mobile }) =>
    collapsed && !mobile ? (
      <div className="flex justify-center">
        <LogoIcon />
      </div>
    ) : (
      <div className="flex items-center gap-3">
        <LogoIcon />
        <span className="font-bold">App Name</span>
      </div>
    )
  }
</SidebarHeader>

Static children still work unchanged — this is purely opt-in.

Accessibility

  • Keyboard support: Escape key dismisses mobile drawer, tab order strictly maintained visually.
  • ARIA attributes: Includes navigation landmarks (aria-label), aria-hidden overlays, and correct semantic lists.
  • Focus behavior: Implements a focus trap within the mobile overlay to ensure screen readers don't escape.

Screen reader behavior

Screen readers announce the container as a "Main navigation" landmark. The sidebar dynamically skips from accessibility trees when fully collapsed or hidden on smaller viewpoints to avoid irrelevant noise.

Component Folder Structure

Sidebar/
├── Sidebar.tsx
├── SidebarContent.tsx
├── SidebarFooter.tsx
├── SidebarHeader.tsx
├── SidebarMenu.tsx
├── SidebarMenuItem.tsx
├── SidebarMobileMenuButton.tsx
├── SidebarToggle.tsx
├── hooks.ts
├── index.ts
└── types.ts

Props

SidebarToggle

PropTypeDefaultDescription
floatingbooleantruetrue = absolute floating handle on the sidebar edge. false = plain inline button.

SidebarHeader

PropTypeDefaultDescription
childrenReactNode | ((state: { collapsed, mobile }) => ReactNode)Header content. Pass a function to react to rail state.

SidebarFooter

PropTypeDefaultDescription
childrenReactNode | ((state: { collapsed, mobile }) => ReactNode)Footer content. Pass a function to react to rail state.

SidebarMenuItem

PropTypeDefaultDescription
iconReactNode-Visual element placed to the left of the item text.
activebooleanfalseMarks the item as the current active route.
badgestring | number-Notice or counter displayed at the right edge.
hrefstring-Hyperlink target destination.
subItemsSubItem[]-Array of nested children { label, active, href } to render a dropdown.

SidebarMenuGroup

PropTypeDefaultDescription
labelstring-Heading label for the grouping section.

On this page