Skip to content

Navigation

Menubar

A horizontal bar of menus that behaves like one desktop-style menu strip.

When to use it: Use it when an application area offers several persistent groups of commands.

On this page

Example

Menubar.tsxtsx
import { Menu, MenuItem, MenuList, MenuPopover, MenuSeparator, MenuTrigger } from "@comp0/react";
import { Menubar } from "@comp0/react";

export function Example() {
  return (
    <Menubar
      aria-label="Notes"
      className="flex items-center gap-1 rounded border border-zinc-950/10 p-1 dark:border-white/10"
    >
      <Menu>
        <MenuTrigger className="rounded px-3 py-2.5 text-base text-zinc-800 select-none hover:bg-zinc-100 data-open:bg-zinc-100 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800 dark:data-open:bg-zinc-800">
          File
        </MenuTrigger>
        <MenuPopover
          placement="bottom start"
          offset={4}
          className="w-44 rounded border-0 bg-white p-1 shadow-lg ring-1 ring-zinc-950/10 dark:bg-zinc-900 dark:shadow-none dark:ring-white/10"
        >
          <MenuList>
            <MenuItem
              className="w-full rounded px-3 py-2.5 text-left text-base text-zinc-800 select-none hover:bg-zinc-100 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800"
              value="new"
            >
              New note
            </MenuItem>
            <MenuItem
              className="w-full rounded px-3 py-2.5 text-left text-base text-zinc-800 select-none hover:bg-zinc-100 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800"
              value="duplicate"
            >
              Duplicate
            </MenuItem>
            <MenuSeparator className="my-1 h-px bg-zinc-950/10 dark:bg-white/10" />
            <MenuItem
              className="w-full rounded px-3 py-2.5 text-left text-base text-zinc-800 select-none hover:bg-zinc-100 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800"
              value="delete"
            >
              Delete
            </MenuItem>
          </MenuList>
        </MenuPopover>
      </Menu>
      <Menu>
        <MenuTrigger className="rounded px-3 py-2.5 text-base text-zinc-800 select-none hover:bg-zinc-100 data-open:bg-zinc-100 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800 dark:data-open:bg-zinc-800">
          Edit
        </MenuTrigger>
        <MenuPopover
          placement="bottom start"
          offset={4}
          className="w-44 rounded border-0 bg-white p-1 shadow-lg ring-1 ring-zinc-950/10 dark:bg-zinc-900 dark:shadow-none dark:ring-white/10"
        >
          <MenuList>
            <MenuItem
              className="w-full rounded px-3 py-2.5 text-left text-base text-zinc-800 select-none hover:bg-zinc-100 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800"
              value="undo"
            >
              Undo
            </MenuItem>
            <MenuItem
              className="w-full rounded px-3 py-2.5 text-left text-base text-zinc-800 select-none hover:bg-zinc-100 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800"
              value="redo"
            >
              Redo
            </MenuItem>
          </MenuList>
        </MenuPopover>
      </Menu>
      <Menu>
        <MenuTrigger className="rounded px-3 py-2.5 text-base text-zinc-800 select-none hover:bg-zinc-100 data-open:bg-zinc-100 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800 dark:data-open:bg-zinc-800">
          View
        </MenuTrigger>
        <MenuPopover
          placement="bottom start"
          offset={4}
          className="w-44 rounded border-0 bg-white p-1 shadow-lg ring-1 ring-zinc-950/10 dark:bg-zinc-900 dark:shadow-none dark:ring-white/10"
        >
          <MenuList>
            <MenuItem
              className="w-full rounded px-3 py-2.5 text-left text-base text-zinc-800 select-none hover:bg-zinc-100 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800"
              value="zoom-in"
            >
              Zoom in
            </MenuItem>
            <MenuItem
              className="w-full rounded px-3 py-2.5 text-left text-base text-zinc-800 select-none hover:bg-zinc-100 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800"
              value="zoom-out"
            >
              Zoom out
            </MenuItem>
          </MenuList>
        </MenuPopover>
      </Menu>
    </Menubar>
  );
}

Anatomy

Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.

A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.

  1. Menubar

    Horizontal bar of menus that shares one tab stop; while a menu is open, openness follows focus across the bar. Owns a DOM element.

  2. Menu

    Open-state provider for one bar item; the same Menu component used standalone. Does not add a DOM element.

  3. MenuTrigger

    Inside a Menubar it renders as the bar's menuitem and joins the roving tab stop. Owns a DOM element.

  4. MenuPopover

    Floating surface opened below its bar item. Owns a DOM element.

  5. MenuListOptional

    Menu collection inside the surface. Owns a DOM element.

  6. MenuSeparatorOptional

    Rule between groups of items. Owns a DOM element.

  7. MenuItem

    Action item. Owns a DOM element.

Step by step

  1. 1

    Add the main part

    Wrap your Menu components in Menubar and give the bar an aria-label.

  2. 2

    Add the supporting parts

    Each Menu keeps its usual MenuTrigger, MenuPopover, and MenuList; the trigger becomes the bar's menuitem automatically.

  3. 3

    Make the behavior clear

    Arrow keys rove the bar with one tab stop, and once a menu is open the openness follows focus to its neighbors.

    Exampletsx
    <Menubar aria-label="Notes"><Menu><MenuTrigger>File</MenuTrigger><MenuPopover><MenuList><MenuItem>New note</MenuItem></MenuList></MenuPopover></Menu></Menubar>

Keyboard

Moves into the bar to the last-used item; Tab again leaves.
Moves to the next item, wrapping; while a menu is open, opens the neighbor's menu.
Moves to the previous item, wrapping; while a menu is open, opens the neighbor's menu.
Opens the focused item's menu and focuses the first item.
Opens the focused item's menu and focuses the first item.
Space
Opens the focused item's menu and focuses the first item.
Home
Moves to the first item.
End
Moves to the last item.
Esc
Closes the open menu and returns focus to its bar item. · open menu
Opens the focused submenu item. · submenu
Closes the submenu and refocuses its item. · submenu

Forms and accessibility

No native form behavior.

Accessibility checklist

  • Give the menubar an aria-label naming the area it commands, such as Notes.
  • Keep the bar for persistent application commands; a row of unrelated buttons should be a toolbar instead.
  • Keep bar items in a visual order that matches the arrow-key order.

API reference

Importtsx
import { Menu, MenuItem, MenuList, MenuPopover, MenuSeparator, MenuTrigger, Menubar } from "@comp0/react";

Menubar

DOM element

Horizontal bar of menus that shares one tab stop; while a menu is open, openness follows focus across the bar.

PropTypeDescription
aria-labelstringNames the bar after the application area it commands, such as Notes.

Menu

Context only

Open-state provider for one bar item; the same Menu component used standalone.

PropTypeDescription
open / defaultOpenbooleanControlled or initial open state.
onToggle(open: boolean) => voidReceives the next open state.

MenuTrigger

DOM element

Inside a Menubar it renders as the bar's menuitem and joins the roving tab stop.

PropTypeDescription
disabledbooleanDisables opening and skips the item when arrowing.

MenuPopover

DOM element

Floating surface opened below its bar item.

PropTypeDescription
placementPopoverPlacementTrigger side to open on, usually "bottom start"; flips when there is no room.
offsetnumberPixel gap between the bar item and the menu.

MenuList

OptionalDOM element

Menu collection inside the surface.

MenuSeparator

OptionalDOM element

Rule between groups of items.

MenuItem

DOM element

Action item.

PropTypeDescription
onClick(event) => voidRuns the action; preventDefault keeps the menu open.
valuestringOptional identity for typeahead and data-value.
disabledbooleanDisables the action.

Style hooks

Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.

[data-open]

on MenuTrigger, MenuPopover

That menu is open.
:popover-open

on MenuPopover

Native pseudo-class equivalent.
:focus-visible

on MenuTrigger, MenuItem

The item has visible keyboard focus.
[data-disabled]

on MenuTrigger, MenuItem

The item is disabled.

Keep exploring