Skip to content

Menubar

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.

Navigationv0.1.0experimentalWCAG 2.2 AAView spec
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 menubar

Copies 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.json

Works, 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.

PropTypeDefault
label

The accessible name of the menu bar, e.g. 'Application'.

string"Application"
childrenrequired

MenubarMenu elements, one per top-level menu.

nodenone

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