Menubar
FreeFile, Edit and View for an app that works like a desktop program: one highlight glides along the bar, and once a menu is open, moving along opens the next.
pnpm dlx shadcn@latest add https://caliperui.site/r/menubar.jsonField notes from the October close. Revenue landed two days early; the Lisbon office is now on the shared ledger.
Last command: Nothing yet
When to use #
Use it for
- Editors and tools with many commands grouped by menu.
- Desktop web apps where people expect menus with shortcuts.
Skip it when
- A handful of actions; use buttons or a dropdown menu.
- Site navigation; use the navigation menu.
Installation #
pnpm dlx shadcn@latest add https://caliperui.site/r/menubar.json
Adds its 5 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage #
This is the code behind the preview above. It is built from Menubar, MenubarMenu, MenubarTrigger, MenubarContent, MenubarItem, MenubarCheckboxItem, MenubarRadioGroup, MenubarRadioItem, MenubarGroup, MenubarLabel, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubTrigger and MenubarSubContent.
import { useState } from "react" import { Menubar, MenubarCheckboxItem, MenubarContent, MenubarItem, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger } from "@/components/ui/menubar" const shortcuts = [ { action: "Search everything", keys: "mod+k" }, { action: "New issue", keys: "c" }, { action: "Go to inbox", keys: ["g", "i"], then: true }, { action: "Send comment", keys: "mod+enter" }, { action: "Move selection", keys: ["alt", "up"] }, { action: "Close panel", keys: "esc" }, ]; export function MenubarDemo() { const [ruler, setRuler] = useState(true); const [count, setCount] = useState(false); const [zoom, setZoom] = useState("100"); const [last, setLast] = useState("Nothing yet"); const run = (label: string) => () => setLast(label); const words = "Field notes from the October close. Revenue landed two days early; the Lisbon office is now on the shared ledger."; return ( <div className="grid w-full max-w-2xl gap-3"> <Menubar> <MenubarMenu> <MenubarTrigger>File</MenubarTrigger> <MenubarContent> <MenubarItem onSelect={run("New document")}>New document <MenubarShortcut keys="mod+n" /></MenubarItem> <MenubarItem onSelect={run("Open")}>Open… <MenubarShortcut keys="mod+o" /></MenubarItem> <MenubarSub> <MenubarSubTrigger>Open recent</MenubarSubTrigger> <MenubarSubContent> <MenubarItem onSelect={run("Opened October close")}>October close</MenubarItem> <MenubarItem onSelect={run("Opened Hiring plan")}>Hiring plan 2027</MenubarItem> <MenubarItem onSelect={run("Opened Board memo")}>Board memo</MenubarItem> </MenubarSubContent> </MenubarSub> <MenubarSeparator /> <MenubarItem onSelect={run("Saved")}>Save <MenubarShortcut keys="mod+s" /></MenubarItem> <MenubarSub> <MenubarSubTrigger>Export as</MenubarSubTrigger> <MenubarSubContent> <MenubarItem onSelect={run("Exported PDF")}>PDF</MenubarItem> <MenubarItem onSelect={run("Exported Markdown")}>Markdown</MenubarItem> <MenubarItem onSelect={run("Exported Word")}>Word document</MenubarItem> </MenubarSubContent> </MenubarSub> <MenubarSeparator /> <MenubarItem disabled>Print… <MenubarShortcut keys="mod+p" /></MenubarItem> </MenubarContent> </MenubarMenu> <MenubarMenu> <MenubarTrigger>Edit</MenubarTrigger> <MenubarContent> <MenubarItem onSelect={run("Undo")}>Undo <MenubarShortcut keys="mod+z" /></MenubarItem> <MenubarItem onSelect={run("Redo")}>Redo <MenubarShortcut keys="mod+shift+z" /></MenubarItem> <MenubarSeparator /> <MenubarItem onSelect={run("Cut")}>Cut <MenubarShortcut keys="mod+x" /></MenubarItem> <MenubarItem onSelect={run("Copy")}>Copy <MenubarShortcut keys="mod+c" /></MenubarItem> <MenubarItem onSelect={run("Paste")}>Paste <MenubarShortcut keys="mod+v" /></MenubarItem> <MenubarSeparator /> <MenubarItem onSelect={run("Find")}>Find… <MenubarShortcut keys="mod+f" /></MenubarItem> </MenubarContent> </MenubarMenu> <MenubarMenu> <MenubarTrigger>View</MenubarTrigger> <MenubarContent> <MenubarCheckboxItem checked={ruler} onCheckedChange={setRuler}>Show ruler</MenubarCheckboxItem> <MenubarCheckboxItem checked={count} onCheckedChange={setCount}>Show word count</MenubarCheckboxItem> <MenubarSeparator /> <MenubarRadioGroup value={zoom} onValueChange={setZoom}> <MenubarRadioItem value="75">Zoom 75%</MenubarRadioItem> <MenubarRadioItem value="100">Zoom 100%</MenubarRadioItem> <MenubarRadioItem value="125">Zoom 125%</MenubarRadioItem> </MenubarRadioGroup> </MenubarContent> </MenubarMenu> <MenubarMenu> <MenubarTrigger>Help</MenubarTrigger> <MenubarContent> <MenubarItem onSelect={run("Opened shortcuts")}>Keyboard shortcuts <MenubarShortcut keys="mod+/" /></MenubarItem> <MenubarItem onSelect={run("Opened what's new")}>What’s new</MenubarItem> </MenubarContent> </MenubarMenu> </Menubar> <div className="grid gap-2 rounded-[12px] border border-border p-4"> {ruler && <div aria-hidden="true" className="h-3 bg-[repeating-linear-gradient(90deg,var(--border-strong)_0_1px,transparent_1px_24px)] opacity-60" />} <p className="max-w-[60ch] leading-relaxed" style={{ fontSize: `${(14 * Number(zoom)) / 100}px` }}>{words}</p> {count && <p className="text-xs text-muted-foreground">{words.split(/\s+/u).length} words</p>} </div> <p role="status" className="text-[13px] text-muted-foreground">Last command: {last}</p> </div> ); }
API reference #
Menubar
component
No props of its own; it accepts the native props of its root element.
MenubarMenu
component
No props of its own; it accepts the native props of its root element.
MenubarTrigger
component
No props of its own; it accepts the native props of its root element.
MenubarContent
component
No props of its own; it accepts the native props of its root element.
MenubarItem
component
No props of its own; it accepts the native props of its root element.
MenubarCheckboxItem
component
No props of its own; it accepts the native props of its root element.
MenubarRadioGroup
component
No props of its own; it accepts the native props of its root element.
MenubarRadioItem
component
No props of its own; it accepts the native props of its root element.
MenubarGroup
component
No props of its own; it accepts the native props of its root element.
MenubarLabel
component
No props of its own; it accepts the native props of its root element.
MenubarSeparator
component
No props of its own; it accepts the native props of its root element.
MenubarShortcut
component
MenubarSub
component
No props of its own; it accepts the native props of its root element.
MenubarSubTrigger
component
No props of its own; it accepts the native props of its root element.
MenubarSubContent
component
No props of its own; it accepts the native props of its root element.
Accessibility #
It is built on native elements and keeps aria-hidden in step with state.
Keyboard and focus handling comes from Radix dropdown-menu and menubar: arrow keys move through items, Enter and Space select, and Escape closes and returns focus.
Motion #
It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.