HextaUI

Menubar

A desktop-style bar of menus with one highlight that slides between open menus and instant switching as you sweep across.

import { KbdGroup } from "@/components/ui/kbd"
import {
  Menubar,
  MenubarCheckboxItem,
  MenubarContent,
  MenubarGroup,
  MenubarItem,
  MenubarLabel,
  MenubarMenu,
  MenubarRadioGroup,
  MenubarRadioItem,
  MenubarSeparator,
  MenubarShortcut,
  MenubarSub,
  MenubarSubContent,
  MenubarSubTrigger,
  MenubarTrigger,
} from "@/components/ui/menubar"

export function MenubarDemo() {
  return (
    <Menubar aria-label="Editor">
      <MenubarMenu>
        <MenubarTrigger>File</MenubarTrigger>
        <MenubarContent>
          <MenubarItem>
            New tab
            <MenubarShortcut>
              <KbdGroup keys="mod+t" variant="flat" size="sm" />
            </MenubarShortcut>
          </MenubarItem>
          <MenubarItem>
            New window
            <MenubarShortcut>
              <KbdGroup keys="mod+n" variant="flat" size="sm" />
            </MenubarShortcut>
          </MenubarItem>
          <MenubarItem disabled>New incognito window</MenubarItem>
          <MenubarSeparator />
          <MenubarSub>
            <MenubarSubTrigger>Share</MenubarSubTrigger>
            <MenubarSubContent>
              <MenubarItem>Email link</MenubarItem>
              <MenubarItem>Messages</MenubarItem>
              <MenubarItem>Notes</MenubarItem>
            </MenubarSubContent>
          </MenubarSub>
          <MenubarSeparator />
          <MenubarItem>
            Print…
            <MenubarShortcut>
              <KbdGroup keys="mod+p" variant="flat" size="sm" />
            </MenubarShortcut>
          </MenubarItem>
        </MenubarContent>
      </MenubarMenu>
      <MenubarMenu>
        <MenubarTrigger>Edit</MenubarTrigger>
        <MenubarContent>
          <MenubarItem>
            Undo
            <MenubarShortcut>
              <KbdGroup keys="mod+z" variant="flat" size="sm" />
            </MenubarShortcut>
          </MenubarItem>
          <MenubarItem>
            Redo
            <MenubarShortcut>
              <KbdGroup keys="mod+shift+z" variant="flat" size="sm" />
            </MenubarShortcut>
          </MenubarItem>
          <MenubarSeparator />
          <MenubarSub>
            <MenubarSubTrigger>Find</MenubarSubTrigger>
            <MenubarSubContent>
              <MenubarItem>Search the web</MenubarItem>
              <MenubarSeparator />
              <MenubarItem>Find…</MenubarItem>
              <MenubarItem>Find next</MenubarItem>
              <MenubarItem>Find previous</MenubarItem>
            </MenubarSubContent>
          </MenubarSub>
          <MenubarSeparator />
          <MenubarItem>Cut</MenubarItem>
          <MenubarItem>Copy</MenubarItem>
          <MenubarItem>Paste</MenubarItem>
        </MenubarContent>
      </MenubarMenu>
      <MenubarMenu>
        <MenubarTrigger>View</MenubarTrigger>
        <MenubarContent>
          <MenubarCheckboxItem>Always show bookmarks bar</MenubarCheckboxItem>
          <MenubarCheckboxItem defaultChecked>
            Always show full URLs
          </MenubarCheckboxItem>
          <MenubarSeparator />
          <MenubarItem inset>
            Reload
            <MenubarShortcut>
              <KbdGroup keys="mod+r" variant="flat" size="sm" />
            </MenubarShortcut>
          </MenubarItem>
          <MenubarItem inset disabled>
            Force reload
            <MenubarShortcut>
              <KbdGroup keys="mod+shift+r" variant="flat" size="sm" />
            </MenubarShortcut>
          </MenubarItem>
          <MenubarSeparator />
          <MenubarItem inset>Toggle full screen</MenubarItem>
        </MenubarContent>
      </MenubarMenu>
      <MenubarMenu>
        <MenubarTrigger>Profiles</MenubarTrigger>
        <MenubarContent>
          <MenubarGroup>
            <MenubarLabel>Switch profile</MenubarLabel>
            <MenubarRadioGroup defaultValue="mira">
              <MenubarRadioItem value="jun">Jun</MenubarRadioItem>
              <MenubarRadioItem value="mira">Mira</MenubarRadioItem>
              <MenubarRadioItem value="sol">Sol</MenubarRadioItem>
            </MenubarRadioGroup>
          </MenubarGroup>
          <MenubarSeparator />
          <MenubarItem inset>Edit…</MenubarItem>
          <MenubarItem inset>Add profile…</MenubarItem>
        </MenubarContent>
      </MenubarMenu>
    </Menubar>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/menubar.json

Adds the component, the HextaUI theme tokens and any HextaUI components it depends on.

import {
  Menubar,
  MenubarContent,
  MenubarItem,
  MenubarMenu,
  MenubarShortcut,
  MenubarTrigger,
} from "@/components/ui/menubar"
<Menubar aria-label="Editor">
  <MenubarMenu>
    <MenubarTrigger>File</MenubarTrigger>
    <MenubarContent>
      <MenubarItem>
        New tab <MenubarShortcut>⌘T</MenubarShortcut>
      </MenubarItem>
    </MenubarContent>
  </MenubarMenu>
</Menubar>

Each <MenubarMenu /> is a Dropdown menu, so items, checkbox and radio items, submenus, labels and shortcuts work and look exactly the same. Open a menu, then move the pointer or press the arrow keys to sweep across the bar.

Menubar
└── MenubarMenu
    ├── MenubarTrigger
    └── MenubarContent
        ├── MenubarGroup
        │   ├── MenubarLabel
        │   └── MenubarItem
        │       └── MenubarShortcut
        ├── MenubarCheckboxItem
        ├── MenubarRadioGroup
        │   └── MenubarRadioItem
        ├── MenubarSeparator
        └── MenubarSub
            ├── MenubarSubTrigger
            └── MenubarSubContent

Trigger icons

Put an icon before a trigger's label, or use an icon on its own with an aria-label. Icon-only triggers get a square shape.

import {
  IconAdjustmentsHorizontal,
  IconDots,
  IconFolder,
  IconLayoutGrid,
  IconPencil,
} from "@tabler/icons-react"

import {
  Menubar,
  MenubarContent,
  MenubarItem,
  MenubarMenu,
  MenubarSeparator,
  MenubarTrigger,
} from "@/components/ui/menubar"

export function MenubarTriggerIcons() {
  return (
    <Menubar aria-label="Project">
      <MenubarMenu>
        <MenubarTrigger>
          <IconFolder />
          Project
        </MenubarTrigger>
        <MenubarContent>
          <MenubarItem>Open…</MenubarItem>
          <MenubarItem>Duplicate</MenubarItem>
        </MenubarContent>
      </MenubarMenu>
      <MenubarMenu>
        <MenubarTrigger>
          <IconPencil />
          Edit
        </MenubarTrigger>
        <MenubarContent>
          <MenubarItem>Rename</MenubarItem>
          <MenubarItem>Move</MenubarItem>
        </MenubarContent>
      </MenubarMenu>
      <MenubarMenu>
        <MenubarTrigger>
          <IconLayoutGrid />
          View
        </MenubarTrigger>
        <MenubarContent>
          <MenubarItem>Grid</MenubarItem>
          <MenubarItem>List</MenubarItem>
        </MenubarContent>
      </MenubarMenu>
      <MenubarMenu>
        <MenubarTrigger aria-label="More">
          <IconDots />
        </MenubarTrigger>
        <MenubarContent align="end">
          <MenubarItem>
            <IconAdjustmentsHorizontal />
            Preferences
          </MenubarItem>
          <MenubarSeparator />
          <MenubarItem variant="destructive">Delete project</MenubarItem>
        </MenubarContent>
      </MenubarMenu>
    </Menubar>
  )
}

Icons

Put an icon before the label. Use variant="destructive" on items that delete or can't be undone.

import {
  IconCopy,
  IconDownload,
  IconFilePlus,
  IconFolderOpen,
  IconScissors,
  IconTrash,
} from "@tabler/icons-react"

import {
  Menubar,
  MenubarContent,
  MenubarItem,
  MenubarMenu,
  MenubarSeparator,
  MenubarShortcut,
  MenubarTrigger,
} from "@/components/ui/menubar"

export function MenubarIcons() {
  return (
    <Menubar aria-label="Files">
      <MenubarMenu>
        <MenubarTrigger>File</MenubarTrigger>
        <MenubarContent>
          <MenubarItem>
            <IconFilePlus />
            New file
            <MenubarShortcut>⌘N</MenubarShortcut>
          </MenubarItem>
          <MenubarItem>
            <IconFolderOpen />
            Open…
            <MenubarShortcut>⌘O</MenubarShortcut>
          </MenubarItem>
          <MenubarItem>
            <IconDownload />
            Export
          </MenubarItem>
          <MenubarSeparator />
          <MenubarItem variant="destructive">
            <IconTrash />
            Move to trash
          </MenubarItem>
        </MenubarContent>
      </MenubarMenu>
      <MenubarMenu>
        <MenubarTrigger>Edit</MenubarTrigger>
        <MenubarContent>
          <MenubarItem>
            <IconScissors />
            Cut
            <MenubarShortcut>⌘X</MenubarShortcut>
          </MenubarItem>
          <MenubarItem>
            <IconCopy />
            Copy
            <MenubarShortcut>⌘C</MenubarShortcut>
          </MenubarItem>
        </MenubarContent>
      </MenubarMenu>
    </Menubar>
  )
}

Disabled

Disable a single menu with disabled on <MenubarMenu />, or the whole bar on <Menubar />. Arrow keys skip disabled menus.

import {
  Menubar,
  MenubarContent,
  MenubarItem,
  MenubarMenu,
  MenubarTrigger,
} from "@/components/ui/menubar"

export function MenubarDisabled() {
  return (
    <div className="flex flex-col items-center gap-4">
      <Menubar aria-label="Document">
        <MenubarMenu>
          <MenubarTrigger>File</MenubarTrigger>
          <MenubarContent>
            <MenubarItem>Save</MenubarItem>
          </MenubarContent>
        </MenubarMenu>
        <MenubarMenu disabled>
          <MenubarTrigger>Review</MenubarTrigger>
          <MenubarContent>
            <MenubarItem>Accept all changes</MenubarItem>
          </MenubarContent>
        </MenubarMenu>
        <MenubarMenu>
          <MenubarTrigger>Help</MenubarTrigger>
          <MenubarContent>
            <MenubarItem>Keyboard shortcuts</MenubarItem>
          </MenubarContent>
        </MenubarMenu>
      </Menubar>
      <Menubar aria-label="Locked document" disabled>
        <MenubarMenu>
          <MenubarTrigger>File</MenubarTrigger>
          <MenubarContent>
            <MenubarItem>Save</MenubarItem>
          </MenubarContent>
        </MenubarMenu>
        <MenubarMenu>
          <MenubarTrigger>Edit</MenubarTrigger>
          <MenubarContent>
            <MenubarItem>Undo</MenubarItem>
          </MenubarContent>
        </MenubarMenu>
      </Menubar>
    </div>
  )
}

Vertical

orientation="vertical" stacks the menus and switches navigation to the up and down arrows. Open the menus to the side with side="inline-end".

import {
  Menubar,
  MenubarContent,
  MenubarItem,
  MenubarMenu,
  MenubarTrigger,
} from "@/components/ui/menubar"

const menus = [
  { label: "Projects", items: ["All projects", "Starred", "Archived"] },
  { label: "Team", items: ["Members", "Roles", "Invitations"] },
  { label: "Billing", items: ["Plan", "Invoices", "Payment method"] },
]

export function MenubarVertical() {
  return (
    <Menubar aria-label="Workspace" orientation="vertical">
      {menus.map((menu) => (
        <MenubarMenu key={menu.label}>
          <MenubarTrigger>{menu.label}</MenubarTrigger>
          <MenubarContent side="inline-end" align="start" sideOffset={8}>
            {menu.items.map((item) => (
              <MenubarItem key={item}>{item}</MenubarItem>
            ))}
          </MenubarContent>
        </MenubarMenu>
      ))}
    </Menubar>
  )
}

Right to left

Menus, submenus and arrow keys follow the reading direction. Shortcuts stay in keyboard order.

import {
  Menubar,
  MenubarContent,
  MenubarItem,
  MenubarMenu,
  MenubarShortcut,
  MenubarSub,
  MenubarSubContent,
  MenubarSubTrigger,
  MenubarTrigger,
} from "@/components/ui/menubar"

export function MenubarRtl() {
  return (
    <div dir="rtl">
      <Menubar aria-label="المحرر">
        <MenubarMenu>
          <MenubarTrigger>ملف</MenubarTrigger>
          <MenubarContent>
            <MenubarItem>
              علامة تبويب جديدة
              <MenubarShortcut>⌘T</MenubarShortcut>
            </MenubarItem>
            <MenubarSub>
              <MenubarSubTrigger>مشاركة</MenubarSubTrigger>
              <MenubarSubContent>
                <MenubarItem>رابط البريد</MenubarItem>
              </MenubarSubContent>
            </MenubarSub>
          </MenubarContent>
        </MenubarMenu>
        <MenubarMenu>
          <MenubarTrigger>تحرير</MenubarTrigger>
          <MenubarContent>
            <MenubarItem>تراجع</MenubarItem>
            <MenubarItem>إعادة</MenubarItem>
          </MenubarContent>
        </MenubarMenu>
      </Menubar>
    </div>
  )
}
KeyAction
TabMoves focus into the menubar, then out of it.
←→Moves between menus. With a menu open, opens the next one instead.
EnterSpace↓Opens the focused menu.
↑↓Moves between items in an open menu.
→Opens a submenu from its trigger.
EscCloses the open menu and returns focus to its trigger.
  • Give the menubar a name with aria-label. It renders role="menubar" and each trigger is a menu item, so screen readers announce it as an application menu.
  • The bar is a single tab stop. Arrow keys move between menus, so the page's tab order stays short.
  • The sliding highlight is decoration. With reduced motion it just appears on the open menu.

Menu parts accept the same props as their Dropdown menu counterparts.

PropTypeDefault
orientation
"horizontal" | "vertical""horizontal"
loopFocusWrap from the last menu back to the first.
booleantrue
modalBlock page scrolling and outside clicks while a menu is open.
booleantrue
disabled
booleanfalse
render
ReactElement | (props, state) => ReactElement<div>
AttributeDescription
data-slot="menubar"Target the bar in CSS.
data-orientationThe orientation.
data-has-submenu-openPresent while any menu in the bar is open.
data-slot="menubar-highlight"The sliding background behind the open trigger, with data-visible.
--menubar-radiusCorner radius of the bar. Triggers and the highlight derive theirs from it.
PropTypeDefault
open
boolean–
defaultOpen
booleanfalse
onOpenChange
(open: boolean, details) => void–
disabled
booleanfalse
AttributeDescription
data-slot="menubar-trigger"Target triggers in CSS.
data-popup-openPresent while its menu is open.
data-disabledPresent when disabled.
PropTypeDefault
side
"top" | "bottom" | "left" | "right" | "inline-start" | "inline-end""bottom"
align
"start" | "center" | "end""start"
sideOffset
number8
alignOffsetLines the menu's items up with the trigger's label.
number-4
AttributeDescription
data-slot="menubar-content"Target menus in CSS.
data-instant="group"Present when the menu opened by sweeping from another one. It switches without animating.

MenubarItem, MenubarCheckboxItem, MenubarRadioGroup, MenubarRadioItem, MenubarGroup, MenubarLabel, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubTrigger and MenubarSubContent are the Dropdown menu parts with menubar-* slots. See the Dropdown menu API for their props.