Skip to content
Component installs need the registry setup
VegaStack Design

Sidebar Menu Flyout

A sidebar row whose entries open in a menu beside it on hover or press, such as Spaces in the collapsed icon rail.

Status
stable
Since
0.25.90
Accessibility pattern
Menu button; Escape returns focus to the row

Last updated

General

Hover Spaces in the rail, or focus it and press Enter.

Install

Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Sidebar Menu Flyout before installation. Retain the digest printed by this command.

pnpm exec vegastack-design verify sidebar-menu-flyout

Install with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.

pnpm dlx shadcn@4.21.0 add @vegastack/sidebar-menu-flyout

Run the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.

The same command installs the registry items it composes: @vegastack/dropdown-menu, @vegastack/sidebar.

Usage

import { SidebarMenuFlyout } from "@/components/ui/sidebar-menu-flyout";

<SidebarMenu>
  <SidebarMenuFlyout label="Spaces" icon={<LayoutGrid />} railOnly>
    {spaces.map((space) => (
      <DropdownMenuItem key={space.id} render={<Link href={space.href} />}>
        {space.name}
      </DropdownMenuItem>
    ))}
    <DropdownMenuSeparator />
    <DropdownMenuItem render={<Link href="/spaces/new" />}>
      <Plus />
      New space
    </DropdownMenuItem>
  </SidebarMenuFlyout>
</SidebarMenu>;

A sidebar row whose entries open beside it rather than under it. In the collapsed icon rail a nested list — the person's spaces — has no room to show inline, so the row keeps one icon and its entries open in a menu at its side, headed by the row's label, when the pointer rests on it or it is pressed. railOnly shows the row in the rail alone, so the expanded sidebar keeps its own inline group and the phone drawer lists the entries in full.

Scope

  • Owns: the row (a SidebarMenuItem with a SidebarMenuButton trigger), opening on hover and press, the menu's place beside the row (under it in the phone drawer), the heading, the rail-only display, and the row's count as on any sidebar row.
  • Does not own: the entries — they are the host's DropdownMenuItems, links through render.
  • Compose with: Sidebar's SidebarMenu; DropdownMenu's items and separators; AppShell's railSeparators.

Examples

Spaces in the icon rail

Hover Spaces in the rail, or focus it and press Enter. Expand the sidebar with the header button: the flyout row goes and the inline Spaces group shows instead.

General

Hover Spaces in the rail, or focus it and press Enter.

API Reference

PropTypeDefaultDescription
children*React.ReactNode—The flyout's entries: DropdownMenuItems (links through render={<Link href="…" />}), with DropdownMenuSeparators between sets ("+ New space" last).
icon*React.ReactNode—The row's icon — all that shows of it in the icon rail.
label*string—The row's name: the trigger's label, its tooltip, and the flyout's heading.
badgeReact.ReactNode—A count on the row, as on SidebarMenuButton (a dot or a short pill in the rail).
badgeLabelstring—The count's words in the row's name ("3 unread"), as on SidebarMenuButton.
closeDelaynumber200Milliseconds the flyout stays after the pointer leaves, so it can travel into it.
delaynumber100Milliseconds the pointer rests on the row before the flyout opens.
isActivebooleanfalseThe row is current — one of its entries is the page on screen.
railOnlybooleanfalseShow the row only in the collapsed icon rail — for entries the expanded sidebar lists inline (a Spaces group) and the phone drawer lists in full.

Data attributes and CSS variables on SidebarMenuFlyout

AttributeValues
data-rail-only""
data-slot"sidebar-menu-flyout" | "sidebar-menu-flyout-content" | "sidebar-menu-flyout-label" | "sidebar-menu-flyout-trigger"

Accessibility

  • The row is a menu button named by its label (and its count, as "Spaces 3 new"); the flyout is a menu, its entries menu items.
  • In the collapsed rail the row shows its label as a tooltip on keyboard focus, as every rail row does; the tooltip steps aside while the flyout is open.
  • Hover opens the flyout without taking focus. From the keyboard, Enter or Space opens it with the first entry focused, and Escape closes it and returns focus to the row.
KeyAction
Enter / SpaceOpens the flyout
↓ / ↑Moves between the entries
EnterFollows the entry
EscapeCloses the flyout and returns focus to the row
ContractStates tested
Behaviourdefault, open, hover-open, keyboard-open, rail-only, badge, phone-drawer
Accessibilitymenu-semantics, labeled, focus-return, browser-accessibility-test
Visualdefault, open, active, collapsed

Do / Don't

Do
Reach a nested list from the collapsed rail through one flyout row, and keep the expanded sidebar's inline group with railOnly.
Don't
Hide a whole group in the rail with no way to reach it, or show both the flyout row and the inline group in the expanded sidebar.

On this page