Menubar
An application menu bar, for a workspace with more commands than a toolbar can hold.
Installation
npx shadcn@latest add https://qure-ui.qure.ai/r/menubar.jsonUsage
import {
Menubar, MenubarContent, MenubarItem, MenubarMenu,
MenubarSeparator, MenubarShortcut, MenubarTrigger,
} from '@/components/ui/menubar'<Menubar>
<MenubarMenu>
<MenubarTrigger>Study</MenubarTrigger>
<MenubarContent>
<MenubarItem>
Open prior
<MenubarShortcut>⇧P</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem variant="destructive">Discard draft</MenubarItem>
</MenubarContent>
</MenubarMenu>
</Menubar>Reach for this when a surface has enough commands that a Toolbar would either overflow or hide most of them — a viewer, a report editor. For the two or three actions attached to one thing, use a Dropdown Menu; for the actions attached to a row, a Context Menu.
Composition
<Menubar> {/* the bar; owns roving focus between menus */}
<MenubarMenu> {/* one menu — an ordinary Menu.Root */}
<MenubarTrigger> {/* "Study" */}
<MenubarContent> {/* portal + positioner + popup */}
<MenubarGroup> {/* a labelled run of items */}
<MenubarLabel> {/* the group heading */}
<MenubarItem>
<MenubarSeparator />
</Menubar>Base UI splits this in two: Menubar is only the container, and each menu inside it is a normal
Menu.Root. The container is what makes the bar behave like one control rather than several
buttons — left and right move between menus, and once one menu is open, hovering the next one
swaps to it without a second click.
MenubarLabel is the heading of a group and only of a group. It reads the group's context to
publish its aria-labelledby, so rendering one outside a MenubarGroup throws at runtime —
neither tsc nor the build catches it.
Submenus
MenubarSub nests, and MenubarSubContent already opens to the right with the offsets set. Two
levels is usually the limit worth designing for: past that, the mouse path to the leaf gets long
enough that people give up and use search instead.
Checkbox items
For overlays and other things that are on or off. The indicator column is reserved for every item in the menu, so rows do not shift as things are toggled.
Radio items
Viewport layout: 1x2
For a set where exactly one applies — a viewport layout, a window preset. Wrap them in
MenubarRadioGroup and drive it with value / onValueChange; the group is what makes the set
announce as a set.
Disabled
A whole menu or a single row can be disabled, and both stay in place rather than disappearing. A command that vanishes when it is unavailable teaches the reader that it never existed, so they stop looking for it.
Where you can, say why in the label — "Sign — awaiting consultant review" answers the question the greyed-out row would otherwise raise.
API Reference
MenubarItem
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "destructive" | "default" | destructive tints the row red. |
MenubarContent
| Prop | Type | Default | Description |
|---|---|---|---|
side | "top" | "right" | "bottom" | "left" | "bottom" | Which side of the trigger to open on. |
sideOffset | number | 6 | Gap from the trigger. |
align | "start" | "center" | "end" | "start" | Alignment along that side. |
alignOffset | number | 0 | Nudge along the alignment axis. |
MenubarSubContent is the same component with side="right", sideOffset={4} and
alignOffset={-8}.
Everything else — MenubarCheckboxItem, MenubarRadioGroup, MenubarRadioItem, MenubarSub,
MenubarSubTrigger, MenubarLinkItem — forwards to Base UI unchanged. See the
Base UI Menu and
Menubar documentation for the full API.