{"slug":"menubar","title":"Menubar","description":"Using the menubar machine in your project.","contentType":"component","framework":"react","content":"A menubar groups several menus — like the File / Edit / View bar in a desktop app\n— into a single widget. Each menu is a regular Zag.js menu; the menubar\ncoordinates focus and open state between them.\n\n## Resources\n\n\n[Latest version: v2.0.0-next.1](https://www.npmjs.com/package/@zag-js/menubar)\n[Logic Visualizer](https://zag-visualizer.vercel.app/menubar)\n[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/menubar)\n\n\n\n**Features**\n\n- Acts as a single tab stop, with arrow keys moving focus between menus.\n- Once a menu is open, hovering or arrowing to a sibling switches to it without a\n  click.\n- Typeahead to jump to a menu by typing its label.\n- Works with nested submenus.\n- Horizontal or vertical orientation.\n\n## Installation\n\nInstall the menubar package. You'll use it alongside `@zag-js/menu`.\n\n```bash\nnpm install @zag-js/menubar @zag-js/menu @zag-js/react\n# or\nyarn add @zag-js/menubar @zag-js/menu @zag-js/react\n```\n\n## Usage\n\nA menubar is made of two machines: one `menubar` machine for the bar, and one\n`menu` machine for each top-level menu.\n\n```tsx\nimport * as menu from \"@zag-js/menu\"\nimport * as menubar from \"@zag-js/menubar\"\n```\n\nThe menubar gives each menu a small config object through `api.getMenuContext()`.\nPass it to the menu's `menubar` prop — that's what makes the menu behave as part\nof the bar instead of a standalone dropdown. A context is the easiest way to\nthread it down:\n\n```tsx\nconst MenubarContext = createContext<menu.MenubarContext | undefined>(undefined)\n\nfunction Menu(props: { id: string; label: string; items: string[] }) {\n  const menubarContext = useContext(MenubarContext)\n  const service = useMachine(menu.machine, { id: props.id, menubar: menubarContext })\n  const api = menu.connect(service, normalizeProps)\n\n  return (\n    <>\n      <button {...api.getTriggerProps()}>{props.label}</button>\n      {api.open && (\n        <Portal>\n          <div {...api.getPositionerProps()}>\n            <ul {...api.getContentProps()}>\n              {props.items.map((item) => (\n                <li key={item} {...api.getItemProps({ value: item })}>\n                  {item}\n                </li>\n              ))}\n            </ul>\n          </div>\n        </Portal>\n      )}\n    </>\n  )\n}\n\nfunction Menubar() {\n  const service = useMachine(menubar.machine, { id: useId() })\n  const api = menubar.connect(service, normalizeProps)\n\n  return (\n    <MenubarContext.Provider value={api.getMenuContext()}>\n      <div {...api.getRootProps()}>\n        <Menu id=\"file\" label=\"File\" items={[\"New\", \"Open\", \"Save\"]} />\n        <Menu id=\"edit\" label=\"Edit\" items={[\"Undo\", \"Redo\"]} />\n        <Menu id=\"view\" label=\"View\" items={[\"Zoom In\", \"Zoom Out\"]} />\n      </div>\n    </MenubarContext.Provider>\n  )\n}\n```\n\nThe config carries the menubar's `rootId`, `disabled`, and `orientation`, so a\nmenu always reflects the bar it belongs to.\n\n### Nested submenus\n\nA submenu inside a menubar menu works like any nested menu — link it to its\nparent with `setChild` / `setParent` and render its trigger with\n`getTriggerItemProps`.\n\n```tsx\nconst parent = useMachine(menu.machine, { id: \"edit\", menubar: menubarContext })\nconst sub = useMachine(menu.machine, { id: \"find\" })\n\nuseEffect(() => {\n  parentApi.setChild(sub)\n  subApi.setParent(parent)\n}, [])\n```\n\nOnly the top-level menu gets the `menubar` prop. The submenu doesn't — it's a\nchild of the menu, not of the bar.\n\nWith the keyboard, `ArrowRight` on a submenu trigger opens the submenu, while\n`ArrowRight` on a regular item moves to the next menu in the bar.\n\n### Vertical orientation\n\nSet `orientation` to `\"vertical\"` to stack the triggers. Use `positioning` so the\nmenus open to the side instead of below.\n\n```tsx\nconst service = useMachine(menubar.machine, {\n  id: useId(),\n  orientation: \"vertical\",\n})\n\n// each menu\nconst service = useMachine(menu.machine, {\n  id,\n  menubar: menubarContext,\n  positioning: { placement: \"right-start\" },\n})\n```\n\nIn a vertical menubar, `Up` / `Down` move between triggers, `ArrowRight` opens a\nmenu, and `ArrowLeft` closes it back to its trigger.\n\n### Disabling the menubar\n\nSet `disabled` on the menubar to disable every trigger at once.\n\n```tsx\nconst service = useMachine(menubar.machine, {\n  id: useId(),\n  disabled: true,\n})\n```\n\n### Looping focus\n\nBy default, arrowing past the first or last trigger wraps around. Set\n`loopFocus` to `false` to stop at the ends.\n\n```tsx\nconst service = useMachine(menubar.machine, {\n  id: useId(),\n  loopFocus: false,\n})\n```\n\n## Styling guide\n\nThe menubar root exposes a few attributes you can target in CSS.\n\n```css\n[data-part=\"root\"][data-orientation=\"horizontal|vertical\"] {\n  /* styles per orientation */\n}\n\n[data-part=\"root\"][data-disabled] {\n  /* styles when the whole bar is disabled */\n}\n\n/* present while any menu in the bar is open */\n[data-part=\"root\"][data-has-open-menu=\"true\"] {\n  /* ... */\n}\n```\n\nTriggers are owned by the menu, so style them with the menu's `trigger` part. See\nthe [menu styling guide](/components/menu#styling-guide).\n\n## Methods and Properties\n\n### Machine Context\n\nThe menubar machine exposes the following context properties:\n\n**`ids`**\nType: `ElementIds | undefined`\nDescription: The ids of the elements in the menubar. Useful for composition.\n\n**`orientation`**\nType: `Orientation | undefined`\nDescription: The orientation of the menubar.\n\n**`loopFocus`**\nType: `boolean | undefined`\nDescription: Whether to loop the keyboard navigation across the triggers.\n\n**`disabled`**\nType: `boolean | undefined`\nDescription: Whether the menubar (and all its menu triggers) is disabled.\n\n**`dir`**\nType: `\"ltr\" | \"rtl\" | undefined`\nDescription: The document's text/writing direction.\n\n**`id`**\nType: `string`\nDescription: The unique identifier of the machine.\n\n**`getRootNode`**\nType: `(() => ShadowRoot | Document | Node) | undefined`\nDescription: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.\n\n### Machine API\n\nThe menubar `api` exposes the following methods:\n\n**`hasOpenMenu`**\nType: `boolean`\nDescription: Whether any menu within the menubar is open.\n\n**`orientation`**\nType: `Orientation`\nDescription: The orientation of the menubar.\n\n**`disabled`**\nType: `boolean`\nDescription: Whether the menubar is disabled.\n\n**`getMenuContext`**\nType: `() => MenubarMenuContext`\nDescription: Returns the config to pass to each top-level menu's `menubar` prop. Adapters\nusually expose this via a context so nested `<Menu>`s can read it.\n\n### Data Attributes\n\n**`Root`**\n\n**`data-menubar-root`**: <uid>\n**`data-orientation`**: The orientation of the menubar\n**`data-disabled`**: Present when disabled\n**`data-has-open-menu`**: Present when a menu in the menubar is open\n\n## Accessibility\n\nFollows the\n[WAI-ARIA Menubar pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menubar/) —\nthe bar is a single tab stop and arrow keys move focus between menus.\n\n### Keyboard Interactions\n\n**`ArrowRight + ArrowLeft`**\nDescription: In a horizontal menubar, moves focus to the next/previous menu. If a menu is open, opens the sibling menu.\n\n**`ArrowDown + ArrowUp`**\nDescription: In a vertical menubar, moves focus to the next/previous menu. In a horizontal menubar, opens the focused menu.\n\n**`Enter + Space`**\nDescription: Opens the focused menu and highlights its first item.\n\n**`Home + End`**\nDescription: Moves focus to the first/last menu.\n\n**`Esc`**\nDescription: Closes the open menu and returns focus to its trigger.","package":"@zag-js/menubar","editUrl":"https://github.com/chakra-ui/zag/edit/main/website/data/components/menubar.mdx"}