A horizontal bar of menus like File, Edit and View, as in a desktop application. Each MenubarMenu opens a list of actions with shortcuts, checkbox and radio items and submenus. Arrow keys move between the menus.
import { Menubar, MenubarItem, MenubarMenu, MenubarSeparator, MenubarSubmenu } from "@rdloom/react";
export default function MenubarFileEditViewExample() {
return (
<div className="flex min-h-72 w-full items-start justify-center">
<Menubar label="Application">
<MenubarMenu label="File">
<MenubarItem id="new" shortcut={["Ctrl", "N"]}>New document</MenubarItem>
<MenubarItem id="open" shortcut={["Ctrl", "O"]}>Open</MenubarItem>
<MenubarSubmenu label="Export as">
<MenubarItem id="pdf">PDF</MenubarItem>
<MenubarItem id="html">HTML</MenubarItem>
<MenubarItem id="text">Plain text</MenubarItem>
</MenubarSubmenu>
<MenubarSeparator />
<MenubarItem id="save" shortcut={["Ctrl", "S"]}>Save</MenubarItem>
</MenubarMenu>
<MenubarMenu label="Edit">
<MenubarItem id="undo" shortcut={["Ctrl", "Z"]}>Undo</MenubarItem>
<MenubarItem id="redo" shortcut={["Ctrl", "Shift", "Z"]}>Redo</MenubarItem>
<MenubarSeparator />
<MenubarItem id="find" shortcut={["Ctrl", "F"]}>Find</MenubarItem>
</MenubarMenu>
<MenubarMenu label="View">
<MenubarItem id="zoom-in" shortcut={["Ctrl", "+"]}>Zoom in</MenubarItem>
<MenubarItem id="zoom-out" shortcut={["Ctrl", "-"]}>Zoom out</MenubarItem>
</MenubarMenu>
</Menubar>
</div>
);
}Installation
npx rdloom add menubarCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/menubar.jsonWorks, but without upgrade tracking.
Usage
import { Menubar, MenubarItem, MenubarMenu, MenubarSeparator, MenubarSubmenu } from "@rdloom/react";
<div className="flex min-h-72 w-full items-start justify-center">
<Menubar label="Application">
<MenubarMenu label="File">
<MenubarItem id="new" shortcut={["Ctrl", "N"]}>New document</MenubarItem>
<MenubarItem id="open" shortcut={["Ctrl", "O"]}>Open</MenubarItem>
<MenubarSubmenu label="Export as">
<MenubarItem id="pdf">PDF</MenubarItem>
<MenubarItem id="html">HTML</MenubarItem>
<MenubarItem id="text">Plain text</MenubarItem>
</MenubarSubmenu>
<MenubarSeparator />
<MenubarItem id="save" shortcut={["Ctrl", "S"]}>Save</MenubarItem>
</MenubarMenu>
<MenubarMenu label="Edit">
<MenubarItem id="undo" shortcut={["Ctrl", "Z"]}>Undo</MenubarItem>
<MenubarItem id="redo" shortcut={["Ctrl", "Shift", "Z"]}>Redo</MenubarItem>
<MenubarSeparator />
<MenubarItem id="find" shortcut={["Ctrl", "F"]}>Find</MenubarItem>
</MenubarMenu>
<MenubarMenu label="View">
<MenubarItem id="zoom-in" shortcut={["Ctrl", "+"]}>Zoom in</MenubarItem>
<MenubarItem id="zoom-out" shortcut={["Ctrl", "-"]}>Zoom out</MenubarItem>
</MenubarMenu>
</Menubar>
</div>Checkbox and radio
Showing: toolbar. Density: comfortable.
import { useState } from "react";
import { Menubar, MenubarGroup, MenubarItem, MenubarMenu, MenubarSeparator } from "@rdloom/react";
import type { Key } from "react-aria-components";
export default function MenubarCheckboxAndRadioExample() {
const [panels, setPanels] = useState<Set<Key>>(new Set(["toolbar"]));
const [density, setDensity] = useState<Set<Key>>(new Set(["comfortable"]));
return (
<div className="flex min-h-72 w-full flex-col items-center gap-4">
<Menubar label="Application">
<MenubarMenu label="View">
<MenubarGroup title="Show" selectionMode="multiple" selectedKeys={panels} onSelectionChange={setPanels}>
<MenubarItem id="toolbar">Toolbar</MenubarItem>
<MenubarItem id="sidebar">Sidebar</MenubarItem>
<MenubarItem id="status">Status bar</MenubarItem>
</MenubarGroup>
<MenubarSeparator />
<MenubarGroup title="Density" selectionMode="single" selectedKeys={density} onSelectionChange={setDensity}>
<MenubarItem id="compact">Compact</MenubarItem>
<MenubarItem id="comfortable">Comfortable</MenubarItem>
<MenubarItem id="spacious">Spacious</MenubarItem>
</MenubarGroup>
</MenubarMenu>
</Menubar>
<p className="text-sm text-[var(--rd-color-text-muted)]">
Showing: {[...panels].join(", ") || "nothing"}. Density: {[...density].join("")}.
</p>
</div>
);
}With disabled items
import { Menubar, MenubarItem, MenubarMenu, MenubarSeparator } from "@rdloom/react";
export default function MenubarWithDisabledItemsExample() {
return (
<div className="flex min-h-72 w-full items-start justify-center">
<Menubar label="Application">
<MenubarMenu label="File">
<MenubarItem id="save" shortcut={["Ctrl", "S"]}>Save</MenubarItem>
<MenubarItem id="revert" isDisabled>Revert to saved</MenubarItem>
<MenubarSeparator />
<MenubarItem id="print" shortcut={["Ctrl", "P"]} isDisabled>Print</MenubarItem>
</MenubarMenu>
<MenubarMenu label="Edit">
<MenubarItem id="undo" shortcut={["Ctrl", "Z"]} isDisabled>Undo</MenubarItem>
<MenubarItem id="paste" shortcut={["Ctrl", "V"]}>Paste</MenubarItem>
</MenubarMenu>
</Menubar>
</div>
);
}API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
labelThe accessible name of the menu bar, e.g. 'Application'. | string | "Application" |
childrenrequiredMenubarMenu elements, one per top-level menu. | node | none |
Accessibility
Role menubar, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Tab moves into the bar (one stop) and out of it
- Left and Right Arrow move between menus; with a menu open they open the neighbouring menu
- Down Arrow, Enter or Space opens the menu and focuses its first item
- Up and Down Arrow move between items; Right Arrow opens a submenu and Left Arrow closes it
- Enter or Space chooses an item, or toggles a checkbox or radio item
- Esc closes the menu and returns focus to its trigger
Screen readers announce
- The bar is announced as a menu bar, each trigger as a menu item with a submenu, expanded or collapsed
- Items are announced with their position, and checked items as checked
- Shortcuts are read after the item name
What your code must do
- The bar has role menubar with an accessible name; the triggers are menu items with aria-haspopup and aria-expanded
- Only one trigger is in the tab order at a time
- Focus returns to the trigger when its menu closes
- Checkbox and radio items expose their checked state
- Disabled items stay visible and are skipped by the arrow keys
- Shortcuts shown are only a hint; the app must handle them itself
Guidelines
Use it when
- A web application that works like a desktop editor, with many commands grouped under File, Edit and View
- Commands that already have keyboard shortcuts you want to show
Avoid it when
- Site navigation to pages: use NavigationMenu
- One overflow menu: use Menu
- Few commands: show them as buttons
Don't
- Top-level triggers that run an action directly instead of opening a menu
- Showing a shortcut that nothing handles
- Nesting submenus more than one level deep
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-surface-raised--rd-color-surface-subtle--rd-color-border-default--rd-color-text-default--rd-color-text-muted--rd-color-focus-ring--rd-radius-control--rd-radius-overlay