ComponentsMenu
Menubar
A bar of menus, such as File, Edit and View, for an editor or a tool with many commands.
Import
import { Menubar, MenubarPortal, MenubarMenu, MenubarTrigger, MenubarContent, MenubarGroup, MenubarSeparator, MenubarLabel, MenubarItem, MenubarShortcut, MenubarCheckboxItem, MenubarRadioGroup, MenubarRadioItem, MenubarSub, MenubarSubTrigger, MenubarSubContent } from "@booleanpress/ui/menubar"Usage
A menubar suits an application screen with many commands, such as an email template editor. It is a single tab stop: the arrow keys move between its menus. For links between pages, use a navigation menu; for one menu, a dropdown menu.
import { Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarTrigger } from "@booleanpress/ui/menubar"
export function EditorMenus() {
return (
<Menubar>
<MenubarMenu>
<MenubarTrigger>File</MenubarTrigger>
<MenubarContent>
<MenubarItem onSelect={() => save()}>Save</MenubarItem>
</MenubarContent>
</MenubarMenu>
</Menubar>
)
}Each MenubarMenu holds a MenubarTrigger and its MenubarContent. Once a menu is open, moving the pointer or the arrow keys to another trigger opens that menu instead. The items are the dropdown menu's: MenubarCheckboxItem, MenubarRadioItem inside MenubarRadioGroup, MenubarSub for a nested list, MenubarLabel, MenubarSeparator, MenubarGroup and MenubarShortcut, which shows a key hint without binding the key. A trigger takes an icon before its text. The open menu is controlled with value and onValueChange on Menubar, each MenubarMenu naming itself with value.
Examples
Basic
File, Edit and View menus, with shortcuts, submenus and a disabled item.
import {
Menubar,
MenubarContent,
MenubarItem,
MenubarMenu,
MenubarSeparator,
MenubarShortcut,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from "@booleanpress/ui/menubar"
export default function MenubarBasic() {
return (
<Menubar>
<MenubarMenu>
<MenubarTrigger>File</MenubarTrigger>
<MenubarContent>
<MenubarItem>New template<MenubarShortcut>⌘N</MenubarShortcut></MenubarItem>
<MenubarItem>Open<MenubarShortcut>⌘O</MenubarShortcut></MenubarItem>
<MenubarSeparator />
<MenubarSub>
<MenubarSubTrigger>Export as</MenubarSubTrigger>
<MenubarSubContent>
<MenubarItem>HTML</MenubarItem>
<MenubarItem>Plain text</MenubarItem>
</MenubarSubContent>
</MenubarSub>
<MenubarSeparator />
<MenubarItem>Send a test email<MenubarShortcut>⌘T</MenubarShortcut></MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Edit</MenubarTrigger>
<MenubarContent>
<MenubarItem>Undo<MenubarShortcut>⌘Z</MenubarShortcut></MenubarItem>
<MenubarItem>Redo<MenubarShortcut>⇧⌘Z</MenubarShortcut></MenubarItem>
<MenubarSeparator />
<MenubarSub>
<MenubarSubTrigger>Insert</MenubarSubTrigger>
<MenubarSubContent>
<MenubarItem>Customer name</MenubarItem>
<MenubarItem>Order number</MenubarItem>
<MenubarItem>Unsubscribe link</MenubarItem>
</MenubarSubContent>
</MenubarSub>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>View</MenubarTrigger>
<MenubarContent>
<MenubarItem>Desktop preview</MenubarItem>
<MenubarItem>Mobile preview</MenubarItem>
<MenubarSeparator />
<MenubarItem disabled>Dark mode preview</MenubarItem>
</MenubarContent>
</MenubarMenu>
</Menubar>
)
}Checkbox and radio items
Show or hide panels with checkbox items, and choose one width with radio items.
import { useState } from "react"
import {
Menubar,
MenubarCheckboxItem,
MenubarContent,
MenubarLabel,
MenubarMenu,
MenubarRadioGroup,
MenubarRadioItem,
MenubarSeparator,
MenubarTrigger,
} from "@booleanpress/ui/menubar"
export default function MenubarCheckboxRadio() {
const [panels, setPanels] = useState({ outline: true, variables: false })
const [width, setWidth] = useState("600")
return (
<Menubar>
<MenubarMenu>
<MenubarTrigger>View</MenubarTrigger>
<MenubarContent>
<MenubarLabel>Panels</MenubarLabel>
<MenubarCheckboxItem checked={panels.outline} onCheckedChange={(outline) => setPanels({ ...panels, outline })}>
Outline
</MenubarCheckboxItem>
<MenubarCheckboxItem checked={panels.variables} onCheckedChange={(variables) => setPanels({ ...panels, variables })}>
Variables
</MenubarCheckboxItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Layout</MenubarTrigger>
<MenubarContent>
<MenubarLabel>Email width</MenubarLabel>
<MenubarRadioGroup value={width} onValueChange={setWidth}>
<MenubarRadioItem value="480">480 px</MenubarRadioItem>
<MenubarRadioItem value="600">600 px</MenubarRadioItem>
<MenubarRadioItem value="720">720 px</MenubarRadioItem>
</MenubarRadioGroup>
<MenubarSeparator />
<MenubarCheckboxItem checked disabled>Centre the content</MenubarCheckboxItem>
</MenubarContent>
</MenubarMenu>
</Menubar>
)
}With icons
An icon before each trigger's text and each item's.
import { CopyIcon, EyeIcon, FileIcon, FolderOpenIcon, PencilIcon, RedoIcon, SaveIcon, UndoIcon } from "lucide-react"
import { Menubar, MenubarContent, MenubarItem, MenubarMenu, MenubarSeparator, MenubarTrigger } from "@booleanpress/ui/menubar"
export default function MenubarWithIcons() {
return (
<Menubar>
<MenubarMenu>
<MenubarTrigger><FileIcon />File</MenubarTrigger>
<MenubarContent>
<MenubarItem><FolderOpenIcon />Open</MenubarItem>
<MenubarItem><SaveIcon />Save</MenubarItem>
<MenubarItem><CopyIcon />Duplicate</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger><PencilIcon />Edit</MenubarTrigger>
<MenubarContent>
<MenubarItem><UndoIcon />Undo</MenubarItem>
<MenubarItem><RedoIcon />Redo</MenubarItem>
<MenubarSeparator />
<MenubarItem><CopyIcon />Copy</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger><EyeIcon />View</MenubarTrigger>
<MenubarContent>
<MenubarItem>Desktop preview</MenubarItem>
<MenubarItem>Mobile preview</MenubarItem>
</MenubarContent>
</MenubarMenu>
</Menubar>
)
}Accessibility
- Semantics
- The bar is
role="menubar"; each trigger is amenuitemwitharia-haspopup="menu"andaria-expanded. Each open list isrole="menu", withmenuitem,menuitemcheckboxandmenuitemradioitems; a submenu trigger hasaria-haspopup="menu". - Labels
- Triggers and items are named by their text. Give the bar an
aria-labelwhen the page has more than one menubar. - Focus
- The bar is one tab stop, on the last trigger used. Opening a menu moves focus into it; closing returns focus to its trigger. Focus shows as the
--accentfill. - Known limits
- Only text and icons in items; a menu is not a place for form controls.
- On narrow screens the bar does not collapse into one button. Keep it short, or use a dropdown menu there.
Keyboard
| Key | Behaviour |
|---|---|
| Tab | Moves into the bar, onto one trigger, and out of it again. |
| → | Moves to the next trigger, wrapping; in an open menu, opens the next menu (← in right-to-left pages). |
| ← | Moves to the previous trigger, wrapping; in an open menu, opens the previous menu (→ in right-to-left pages). |
| EnterorSpace | On a trigger, opens its menu. On an item, chooses it and closes the menu. |
| ↓ | On a trigger, opens its menu on the first item. In a menu, moves to the next item. |
| ↑ | In a menu, moves to the previous item. |
| HomeorEnd | Moves to the first or last trigger, or the first or last item in a menu. |
| A–Z | In a menu, moves to the next item whose text starts with the letters typed. |
| Escape | Closes the menu and returns focus to its trigger. |
API
Menubar
Renders Radix Menubar.Root and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | ||
defaultValue | string | The menu open at the start, when it controls itself. | |
dir | "ltr" | "rtl" | The reading direction. The provider's dir by default. | |
loop | boolean | Whether the arrow keys wrap from the last trigger to the first. true by default. | |
onValueChange | ((value: string) => void) | Called with the open menu's value, or an empty string when all close. | |
value | string | The open menu's value, when you control it. Pair it with onValueChange. |
MenubarPortal
Renders Radix Menubar.Portal and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
container | Element | DocumentFragment | null | Specify a container element to portal the content into. | |
forceMount | true | Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. |
MenubarMenu
Renders Radix Menubar.Menu and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
__scopeMenubar | Scope | ||
value | string | The value that names this menu, for a controlled menubar. |
MenubarTrigger
Renders Radix Menubar.Trigger and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | Render the child element instead, with this part's behaviour and classes merged onto it. |
MenubarContent
Renders Radix Menubar.Content and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
align | "center" | "start" | "end" | start | Alignment along the trigger: start (default), center or end. |
alignOffset | number | Distance in pixels along that side. -4 by default, so items line up with the trigger's text. | |
arrowPadding | number | ||
asChild | boolean | ||
avoidCollisions | boolean | ||
collisionBoundary | Boundary | Boundary[] | ||
collisionPadding | number | Partial<Record<"top" | "bottom" | "left" | "right", number>> | ||
forceMount | true | Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. | |
hideWhenDetached | boolean | ||
loop | boolean | Whether keyboard navigation should loop around | |
onCloseAutoFocus | ((event: Event) => void) | Event handler called when auto-focusing on close. Can be prevented. | |
onEscapeKeyDown | ((event: KeyboardEvent) => void) | Called when Escape is pressed. Call event.preventDefault() to keep it open. | |
onFocusOutside | ((event: FocusOutsideEvent) => void) | ||
onInteractOutside | ((event: FocusOutsideEvent | PointerDownOutsideEvent) => void) | ||
onPointerDownOutside | ((event: PointerDownOutsideEvent) => void) | ||
side | "top" | "bottom" | "left" | "right" | bottom | |
sideOffset | number | Distance in pixels below the trigger. 8 by default. | |
sticky | "always" | "partial" | ||
updatePositionStrategy | "always" | "optimized" |
MenubarGroup
Renders Radix Menubar.Group and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean |
MenubarSeparator
Renders Radix Menubar.Separator and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean |
MenubarLabel
Renders Radix Menubar.Label and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | ||
inset | boolean | Indent the label to line up with items that have a check or dot. |
MenubarItem
Renders Radix Menubar.Item and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | Render the child element instead, with this part's behaviour and classes merged onto it. | |
disabled | boolean | Skips the item and ignores clicks. | |
inset | boolean | Indent the item to line up with items that have a check or dot. | |
onSelect | ((event: Event) => void) | Called when the item is chosen. Call event.preventDefault() to keep the menu open. | |
textValue | string | The text type-ahead matches, when the content is not plain text. | |
variant | "default" | "destructive" | default | default, or destructive for an action that removes something. |
MenubarShortcut
Renders a span and passes it every other prop.
MenubarCheckboxItem
Renders Radix Menubar.CheckboxItem and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | ||
checked | boolean | "indeterminate" | Whether it is checked: true, false or "indeterminate". Pair it with onCheckedChange. | |
disabled | boolean | Skips the item and ignores clicks. | |
onCheckedChange | ((checked: boolean) => void) | Called with the new state when it is chosen. | |
onSelect | ((event: Event) => void) | ||
textValue | string |
MenubarRadioGroup
Renders Radix Menubar.RadioGroup and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
valuerequired | string | The chosen item's value. Pair it with onValueChange. | |
asChild | boolean | ||
onValueChange | ((value: string) => void) | Called with the value of the item that was chosen. |
MenubarRadioItem
Renders Radix Menubar.RadioItem and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
valuerequired | string | The value this item stands for. | |
asChild | boolean | ||
disabled | boolean | Skips the item and ignores clicks. | |
onSelect | ((event: Event) => void) | ||
textValue | string |
MenubarSub
Renders Radix Menubar.Sub and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
defaultOpen | boolean | ||
onOpenChange | ((open: boolean) => void) | Called with true or false when it opens or closes. | |
open | boolean | Whether the submenu is open, when you control it. |
MenubarSubTrigger
Renders Radix Menubar.SubTrigger and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | ||
disabled | boolean | Skips the item and ignores clicks. | |
inset | boolean | Indent the item to line up with items that have a check or dot. | |
textValue | string |
MenubarSubContent
Renders Radix Menubar.SubContent and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
align | "start" | "end" | Controls the direction the subcontent appears from its anchor menu item Default: start | |
alignOffset | number | ||
arrowPadding | number | ||
asChild | boolean | ||
avoidCollisions | boolean | ||
collisionBoundary | Boundary | Boundary[] | ||
collisionPadding | number | Partial<Record<"top" | "bottom" | "left" | "right", number>> | ||
forceMount | true | Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. | |
hideWhenDetached | boolean | ||
loop | boolean | Whether keyboard navigation should loop around | |
onEscapeKeyDown | ((event: KeyboardEvent) => void) | ||
onFocusOutside | ((event: FocusOutsideEvent) => void) | ||
onInteractOutside | ((event: FocusOutsideEvent | PointerDownOutsideEvent) => void) | ||
onPointerDownOutside | ((event: PointerDownOutsideEvent) => void) | ||
sideOffset | number | Distance in pixels from its trigger. | |
sticky | "always" | "partial" | ||
updatePositionStrategy | "always" | "optimized" |
Every part takes className, merged with its defaults by cn(), and ref, which reaches the element it renders.
Data attributes: data-slot="menubar" (Menubar), data-slot="menubar-portal" (MenubarPortal), data-slot="menubar-menu" (MenubarMenu), data-slot="menubar-trigger" (MenubarTrigger), data-slot="menubar-content" (MenubarContent), data-slot="menubar-group" (MenubarGroup), data-slot="menubar-separator" (MenubarSeparator), data-slot="menubar-label" (MenubarLabel), data-slot="menubar-item" (MenubarItem), data-slot="menubar-shortcut" (MenubarShortcut), data-slot="menubar-checkbox-item" (MenubarCheckboxItem), data-slot="menubar-radio-group" (MenubarRadioGroup), data-slot="menubar-radio-item" (MenubarRadioItem), data-slot="menubar-sub" (MenubarSub), data-slot="menubar-sub-trigger" (MenubarSubTrigger), data-slot="menubar-sub-content" (MenubarSubContent), and data-inset, data-variant.
Theming
The bar is --card with a --border edge. A hovered, focused or open trigger, and the highlighted item, use --accent. Menus are --popover; icons, checks and dots use --control-hover, and --muted-foreground when highlighted; labels use --muted-foreground.
The tokens its classes read. Change their values in your theme, and it follows.
| Token | Used for |
|---|---|
--accent | background |
--accent-foreground | text |
--border | background |
--card | background |
--card-foreground | text |
--control-hover | text |
--destructive-strong | text |
--destructive-subtle | background |
--muted-foreground | text |
--popover | background |
--popover-foreground | text |