shell · Shell

TopBar

@devalok/shilp-sutra/shell/top-barView in Storybook
Live preview coming

Hand-curated previews ship in rolling waves. See it live in Storybook →

Reference

Overview

Composition-based application top bar. Uses dot-notation subcomponents for flexible layout.

Subcomponents

ComponentPurpose
TopBarRoot — bg-surface-2, border-b, sticky. Auto-switches to grid when Center is present.
TopBar.LeftLeft zone — sidebar trigger, title, breadcrumbs
TopBar.CenterOptional center zone — search bar, tabs. Triggers 3-column grid layout.
TopBar.RightRight zone — action buttons, user menu. Gets ml-auto in flex mode.
TopBar.SectionGroups items with configurable gap
TopBar.IconButtonCircular icon button with tooltip (bg-surface-3, hover:bg-surface-4)
TopBar.TitlePage title heading, hidden on mobile
TopBar.UserMenuAvatar dropdown with color mode toggle, profile, logout

Props

TopBar (root)

children: ReactNode
className?: string

TopBar.Left / TopBar.Center / TopBar.Right

children: ReactNode
className?: string

TopBar.Section

gap?: "tight" | "default" | "loose" (default: "default")
children: ReactNode
className?: string

Gap values: tight = gap-ds-02, default = gap-ds-04, loose = gap-ds-06

TopBar.IconButton

icon: ReactNode
tooltip: string
...ButtonHTMLAttributes

TopBar.Title

children: ReactNode
className?: string

TopBar.UserMenu

user: TopBarUser — { name, email?, image? }
onNavigate?: (path: string) => void
onLogout?: () => void
userMenuItems?: UserMenuItem[]
className?: string

TopBarUser: { name: string, email?: string, image?: string | null } UserMenuItem: { label: string, icon?: ReactNode, href?: string, onClick?: () => void, separator?: boolean, color?: string, badge?: string | boolean, disabled?: boolean }

UserMenuItem fields:

  • href — navigates via onNavigate callback
  • onClick — custom action (takes precedence over href)
  • separator — renders a DropdownMenuSeparator before this item
  • color — semantic text color (e.g. "error" for text-error)
  • badge — string for count badge, true for dot indicator
  • disabled — greys out the item

Example

Two-zone (standard)

<TopBar>
  <TopBar.Left>
    <SidebarTrigger />
    <TopBar.Title>Dashboard</TopBar.Title>
  </TopBar.Left>
  <TopBar.Right>
    <TopBar.Section gap="tight">
      <TopBar.IconButton icon={<IconSearch />} tooltip="Search (Ctrl+K)" onClick={openSearch} />
      <NotificationCenter notifications={notifications} />
      <TopBar.IconButton icon={<IconSparkles />} tooltip="AI Chat" onClick={openAI} />
    </TopBar.Section>
    <TopBar.UserMenu
      user={{ name: 'John', email: '[email protected]' }}
      onNavigate={(p) => router.push(p)}
      onLogout={handleLogout}
      userMenuItems={[
        { label: 'Changelog', icon: <IconNews />, href: '/changelog', badge: '3' },
      ]}
    />
  </TopBar.Right>
</TopBar>

Three-zone (centered search bar)

<TopBar>
  <TopBar.Left>
    <SidebarTrigger />
    <TopBar.Title>Dashboard</TopBar.Title>
  </TopBar.Left>
  <TopBar.Center>
    <SearchBarTrigger />
  </TopBar.Center>
  <TopBar.Right>
    <TopBar.Section gap="tight">
      <TopBar.IconButton icon={<IconBell />} tooltip="Notifications" onClick={fn} />
    </TopBar.Section>
    <TopBar.UserMenu user={user} onLogout={logout} />
  </TopBar.Right>
</TopBar>

Composability

  • Composition-based, NOT data-driven. Use dot-notation subcomponents (TopBar.Left, TopBar.Center, TopBar.Right, TopBar.Section, TopBar.IconButton, TopBar.Title, TopBar.UserMenu) to assemble. No "props config" — explicit JSX.
  • Two-zone vs three-zone layout — adding TopBar.Center flips the root from flex to CSS grid (1fr auto 1fr) for true centering. Standard apps are two-zone (Left + Right); dashboards with prominent search are three-zone.
  • Required providers:
    • SidebarProvider — for SidebarTrigger inside TopBar.Left to work
    • LinkProvider — for TopBar.UserMenu's menu items that use href
  • NotificationCenter lives inside TopBar.Right — it's a complete bell+popover component. Drop it in a TopBar.Section next to other icon buttons.
  • UserMenu is composable: userMenuItems inserts custom items between the built-in Profile and the color-mode toggle. Each item can navigate (href), run an action (onClick), or show a separator/badge/color decoration.
  • Responsive hiding: Actions that shouldn't appear on mobile use className="hidden md:flex" on the IconButton — the component doesn't enforce mobile hiding; that's layout responsibility.
  • Pairs with AppSidebar — desktop app shell is typically <TopBar> + <AppSidebar> + main content region.

Gotchas

  • Without TopBar.Center, layout is flex (two-zone). With it, layout switches to CSS grid 1fr auto 1fr for true centering.
  • TopBar.IconButton renders any number of action buttons — no artificial limit. Use responsive hiding (className="hidden md:flex") for mobile.
  • TopBar.UserMenu includes Profile link, color mode toggle, and logout automatically. userMenuItems are inserted between Profile and the toggle.
  • Requires SidebarProvider wrapper for SidebarTrigger to work.

Changes

v0.19.0

  • BREAKING Rewritten as composition API. Old props-based API removed (pageTitle, onSearchClick, onAiChatClick, notificationSlot, mobileLogo props).
  • Added TopBar.Left, TopBar.Center, TopBar.Right zone components
  • Added TopBar.Section with gap prop (tight | default | loose)
  • Added TopBar.IconButton — reusable circular icon button with tooltip
  • Added TopBar.Title — responsive page title (hidden on mobile)
  • Added TopBar.UserMenu — extracted user dropdown as standalone subcomponent
  • Added Auto grid/flex layout detection based on Center zone presence
  • Changed Background elevated from bg-surface-1 to bg-surface-2

v0.27.2

  • Fixed UserMenu email truncation — long emails now truncate instead of overflowing the dropdown

v0.7.0

  • Added userMenuItems prop for custom dropdown items

v0.1.1

  • Changed Decoupled from Next.js via LinkProvider
  • Fixed Added aria-label to search/AI buttons
  • Fixed Added type="button" to search/AI/avatar buttons to prevent form submission

v0.1.0

  • Added Initial release