Menubar

Desktop application style menubar with a row of menu triggers

Usage

Menubar is a desktop-application style menu bar: a horizontal row of triggers (File, Edit, View, …), each of them opens a dropdown. Menubar is built on top of Menu, dropdown content is composed from Menu.Item, Menu.Divider and other Menu components.

import { Menu, Menubar, Text } from '@mantine/core';

function Demo() {
  return (
    <Menubar>
      <Menubar.Menu width={220}>
        <Menubar.Target>File</Menubar.Target>
        <Menubar.Dropdown>
          <Menu.Item rightSection={<Text size="xs" c="dimmed">⌘N</Text>}>New file</Menu.Item>
          <Menu.Item rightSection={<Text size="xs" c="dimmed">⌘⇧N</Text>}>New window</Menu.Item>
          <Menu.Sub>
            <Menu.Sub.Target>
              <Menu.Sub.Item>Open recent</Menu.Sub.Item>
            </Menu.Sub.Target>
            <Menu.Sub.Dropdown>
              <Menu.Item>project-alpha</Menu.Item>
              <Menu.Item>project-beta</Menu.Item>
              <Menu.Item>project-gamma</Menu.Item>
            </Menu.Sub.Dropdown>
          </Menu.Sub>
          <Menu.Divider />
          <Menu.Item rightSection={<Text size="xs" c="dimmed">⌘S</Text>}>Save</Menu.Item>
          <Menu.Item>Save as…</Menu.Item>
        </Menubar.Dropdown>
      </Menubar.Menu>

      <Menubar.Menu width={220}>
        <Menubar.Target>Edit</Menubar.Target>
        <Menubar.Dropdown>
          <Menu.Item rightSection={<Text size="xs" c="dimmed">⌘Z</Text>}>Undo</Menu.Item>
          <Menu.Item rightSection={<Text size="xs" c="dimmed">⌘⇧Z</Text>}>Redo</Menu.Item>
          <Menu.Divider />
          <Menu.Item>Cut</Menu.Item>
          <Menu.Item>Copy</Menu.Item>
          <Menu.Item>Paste</Menu.Item>
        </Menubar.Dropdown>
      </Menubar.Menu>

      <Menubar.Menu width={220}>
        <Menubar.Target>Help</Menubar.Target>
        <Menubar.Dropdown>
          <Menu.Item>Documentation</Menu.Item>
          <Menu.Item>Keyboard shortcuts</Menu.Item>
          <Menu.Item>About</Menu.Item>
        </Menubar.Dropdown>
      </Menubar.Menu>
    </Menubar>
  );
}

How it differs from Menu

Menu is one target that toggles one dropdown. Menubar coordinates multiple menus:

  • Only one menu is open at a time. Moving to another target while a menu is open switches the open menu.
  • The whole bar is a single tab stop, arrow keys move focus between targets.

If you need a single dropdown attached to one button, use Menu instead.

Composition

  • Menubar – root element
  • Menubar.Menu – wraps a single menu, accepts the same props as Menu
  • Menubar.Target – top-level trigger button
  • Menubar.Dropdown – dropdown container, accepts the same children as Menu.Dropdown
import { Menu, Menubar } from '@mantine/core';

function Demo() {
  return (
    <Menubar>
      <Menubar.Menu>
        <Menubar.Target>File</Menubar.Target>
        <Menubar.Dropdown>
          <Menu.Item>New file</Menu.Item>
          <Menu.Item>Open…</Menu.Item>
        </Menubar.Dropdown>
      </Menubar.Menu>

      <Menubar.Menu>
        <Menubar.Target>Edit</Menubar.Target>
        <Menubar.Dropdown>
          <Menu.Item>Undo</Menu.Item>
          <Menu.Item>Redo</Menu.Item>
        </Menubar.Dropdown>
      </Menubar.Menu>
    </Menubar>
  );
}

Trigger and loop

By default, a menu opens when its target is clicked. Once a menu is open, hovering another target switches to it. Set trigger="hover" to open menus on hover, the menu closes when the pointer leaves the bar.

Set loop={false} to stop arrow key navigation from wrapping around from the last menu to the first and vice versa.

import { Menu, Menubar } from '@mantine/core';

function Demo() {
  return (
    <Menubar trigger="hover">
      <Menubar.Menu width={220}>
        <Menubar.Target>File</Menubar.Target>
        <Menubar.Dropdown>
          <Menu.Item>New file</Menu.Item>
          <Menu.Item>New window</Menu.Item>
          <Menu.Item>Save</Menu.Item>
        </Menubar.Dropdown>
      </Menubar.Menu>

      <Menubar.Menu width={220}>
        <Menubar.Target>Edit</Menubar.Target>
        <Menubar.Dropdown>
          <Menu.Item>Undo</Menu.Item>
          <Menu.Item>Redo</Menu.Item>
          <Menu.Item>Cut</Menu.Item>
          <Menu.Item>Copy</Menu.Item>
        </Menubar.Dropdown>
      </Menubar.Menu>

      <Menubar.Menu width={220}>
        <Menubar.Target>View</Menubar.Target>
        <Menubar.Dropdown>
          <Menu.Item>Zoom in</Menu.Item>
          <Menu.Item>Zoom out</Menu.Item>
          <Menu.Item>Reset zoom</Menu.Item>
        </Menubar.Dropdown>
      </Menubar.Menu>
    </Menubar>
  );
}

Submenus

Use Menu.Sub inside Menubar.Dropdown to create nested submenus, see the Menu submenus documentation. The usage demo includes a submenu in the File menu.

Checkbox and radio items

Use Menu.CheckboxItem for toggles and Menu.RadioGroup with Menu.RadioItem for single-choice options. By default, clicking these items does not close the menu.

import { Menu, Menubar } from '@mantine/core';

function Demo() {
  return (
    <Menubar>
      <Menubar.Menu width={220}>
        <Menubar.Target>View</Menubar.Target>
        <Menubar.Dropdown>
          <Menu.CheckboxItem defaultChecked>Show sidebar</Menu.CheckboxItem>
          <Menu.CheckboxItem>Show status bar</Menu.CheckboxItem>
          <Menu.Divider />
          <Menu.Label>Appearance</Menu.Label>
          <Menu.RadioGroup defaultValue="comfortable">
            <Menu.RadioItem value="compact">Compact</Menu.RadioItem>
            <Menu.RadioItem value="comfortable">Comfortable</Menu.RadioItem>
            <Menu.RadioItem value="spacious">Spacious</Menu.RadioItem>
          </Menu.RadioGroup>
        </Menubar.Dropdown>
      </Menubar.Menu>

      <Menubar.Menu width={220}>
        <Menubar.Target>Window</Menubar.Target>
        <Menubar.Dropdown>
          <Menu.Item>Minimize</Menu.Item>
          <Menu.Item>Zoom</Menu.Item>
          <Menu.Item>Bring all to front</Menu.Item>
        </Menubar.Dropdown>
      </Menubar.Menu>
    </Menubar>
  );
}

Controlled

Use openIndex and onOpenChange props to control which menu is open. openIndex is the index of the opened Menubar.Menu or null when all menus are closed.

Use defaultOpenIndex to open a menu initially without controlling the state.

import { useState } from 'react';
import { Button, Group, Menu, Menubar, Stack } from '@mantine/core';

function Demo() {
  const [openIndex, setOpenIndex] = useState<number | null>(null);

  return (
    <Stack>
      <Group>
        <Button variant="default" onClick={() => setOpenIndex(0)}>Open File</Button>
        <Button variant="default" onClick={() => setOpenIndex(1)}>Open Edit</Button>
        <Button variant="default" onClick={() => setOpenIndex(null)}>Close all</Button>
      </Group>

      <Menubar openIndex={openIndex} onOpenChange={setOpenIndex}>
        <Menubar.Menu width={220}>
          <Menubar.Target>File</Menubar.Target>
          <Menubar.Dropdown>
            <Menu.Item>New file</Menu.Item>
            <Menu.Item>Save</Menu.Item>
          </Menubar.Dropdown>
        </Menubar.Menu>

        <Menubar.Menu width={220}>
          <Menubar.Target>Edit</Menubar.Target>
          <Menubar.Dropdown>
            <Menu.Item>Undo</Menu.Item>
            <Menu.Item>Redo</Menu.Item>
          </Menubar.Dropdown>
        </Menubar.Menu>
      </Menubar>
    </Stack>
  );
}

Position

By default, dropdowns are positioned at bottom-start. Set the position prop to use a different Floating UI position for all menus in the bar:

import { Menubar } from '@mantine/core';

function Demo() {
  return (
    <Menubar position="bottom-end">
      {/* ...menus */}
    </Menubar>
  );
}

To override settings of a single menu, pass Menu props to Menubar.Menu, for example position or closeOnItemClick.

Keyboard interactions

When RTL direction is set, ArrowLeft and ArrowRight are swapped.

KeyDescription
ArrowRightMove to the next top-level menu, switch the open menu if one is open
ArrowLeftMove to the previous top-level menu, switch the open menu if one is open
ArrowDown / Enter / SpaceOpen the focused menu and move focus to its first item
ArrowUpOpen the focused menu and move focus to its last item
EscapeClose the open menu and return focus to its target
Home / EndMove to the first / last top-level menu
Character keysMove focus to the next top-level menu that starts with the typed character
TabMove focus into or out of the menu bar (single tab stop)

Styles API

Menubar supports the Styles API; you can add styles to any inner element of the component with the classNames prop. Follow the Styles API documentation to learn more.

Component Styles API

Hover over selectors to highlight corresponding elements

/*
 * Hover over selectors to apply outline styles
 *
 */

Accessibility

Menubar follows the WAI-ARIA menubar pattern:

  • The root element has role="menubar"
  • Each Menubar.Target has role="menuitem", aria-haspopup="menu" and aria-expanded
  • Menubar.Target children are used as accessible names of the triggers