A visually persistent menu common in desktop applications that provides quick access to a consistent set of commands.
import React from 'react';
import * as Menubar from '@radix-ui/react-menubar';
import { CheckIcon, ChevronRightIcon, DotFilledIcon } from '@radix-ui/react-icons';
import './styles.css';
const RADIO_ITEMS = ['Andy', 'Benoît', 'Luis'];
const CHECK_ITEMS = ['Always Show Bookmarks Bar', 'Always Show Full URLs'];
const MenubarDemo = () => {
const [checkedSelection, setCheckedSelection] = React.useState([CHECK_ITEMS[1]]);
const [radioSelection, setRadioSelection] = React.useState(RADIO_ITEMS[2]);
return (
<Menubar.Root className="MenubarRoot">
<Menubar.Menu>
<Menubar.Trigger className="MenubarTrigger">File</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content className="MenubarContent" align="start" sideOffset={5} alignOffset={-3}>
<Menubar.Item className="MenubarItem">
New Tab <div className="RightSlot">⌘ T</div>
</Menubar.Item>
<Menubar.Item className="MenubarItem">
New Window <div className="RightSlot">⌘ N</div>
</Menubar.Item>
<Menubar.Item className="MenubarItem" disabled>
New Incognito Window
</Menubar.Item>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Sub>
<Menubar.SubTrigger className="MenubarSubTrigger">
Share
<div className="RightSlot">
<ChevronRightIcon />
</div>
</Menubar.SubTrigger>
<Menubar.Portal>
<Menubar.SubContent className="MenubarSubContent" alignOffset={-5}>
<Menubar.Item className="MenubarItem">Email Link</Menubar.Item>
<Menubar.Item className="MenubarItem">Messages</Menubar.Item>
<Menubar.Item className="MenubarItem">Notes</Menubar.Item>
</Menubar.SubContent>
</Menubar.Portal>
</Menubar.Sub>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem">
Print… <div className="RightSlot">⌘ P</div>
</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
<Menubar.Menu>
<Menubar.Trigger className="MenubarTrigger">Edit</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content className="MenubarContent" align="start" sideOffset={5} alignOffset={-3}>
<Menubar.Item className="MenubarItem">
Undo <div className="RightSlot">⌘ Z</div>
</Menubar.Item>
<Menubar.Item className="MenubarItem">
Redo <div className="RightSlot">⇧ ⌘ Z</div>
</Menubar.Item>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Sub>
<Menubar.SubTrigger className="MenubarSubTrigger">
Find
<div className="RightSlot">
<ChevronRightIcon />
</div>
</Menubar.SubTrigger>
<Menubar.Portal>
<Menubar.SubContent className="MenubarSubContent" alignOffset={-5}>
<Menubar.Item className="MenubarItem">Search the web…</Menubar.Item>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem">Find…</Menubar.Item>
<Menubar.Item className="MenubarItem">Find Next</Menubar.Item>
<Menubar.Item className="MenubarItem">Find Previous</Menubar.Item>
</Menubar.SubContent>
</Menubar.Portal>
</Menubar.Sub>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem">Cut</Menubar.Item>
<Menubar.Item className="MenubarItem">Copy</Menubar.Item>
<Menubar.Item className="MenubarItem">Paste</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
<Menubar.Menu>
<Menubar.Trigger className="MenubarTrigger">View</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content
className="MenubarContent"
align="start"
sideOffset={5}
alignOffset={-14}
>
{CHECK_ITEMS.map((item) => (
<Menubar.CheckboxItem
className="MenubarCheckboxItem inset"
key={item}
checked={checkedSelection.includes(item)}
onCheckedChange={() =>
setCheckedSelection((current) =>
current.includes(item)
? current.filter((el) => el !== item)
: current.concat(item)
)
}
>
<Menubar.ItemIndicator className="MenubarItemIndicator">
<CheckIcon />
</Menubar.ItemIndicator>
{item}
</Menubar.CheckboxItem>
))}
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem inset">
Reload <div className="RightSlot">⌘ R</div>
</Menubar.Item>
<Menubar.Item className="MenubarItem inset" disabled>
Force Reload <div className="RightSlot">⇧ ⌘ R</div>
</Menubar.Item>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem inset">Toggle Fullscreen</Menubar.Item>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem inset">Hide Sidebar</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
<Menubar.Menu>
<Menubar.Trigger className="MenubarTrigger">Profiles</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content
className="MenubarContent"
align="start"
sideOffset={5}
alignOffset={-14}
>
<Menubar.RadioGroup value={radioSelection} onValueChange={setRadioSelection}>
{RADIO_ITEMS.map((item) => (
<Menubar.RadioItem className="MenubarRadioItem inset" key={item} value={item}>
<Menubar.ItemIndicator className="MenubarItemIndicator">
<DotFilledIcon />
</Menubar.ItemIndicator>
{item}
</Menubar.RadioItem>
))}
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem inset">Edit…</Menubar.Item>
<Menubar.Separator className="MenubarSeparator" />
<Menubar.Item className="MenubarItem inset">Add Profile…</Menubar.Item>
</Menubar.RadioGroup>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);
};
export default MenubarDemo;
Install the component from your command line.
npm install @radix-ui/react-menubar
Import all parts and piece them together.
import * as Menubar from '@radix-ui/react-menubar';
export default () => (
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger />
<Menubar.Portal>
<Menubar.Content>
<Menubar.Label />
<Menubar.Item />
<Menubar.Group>
<Menubar.Item />
</Menubar.Group>
<Menubar.CheckboxItem>
<Menubar.ItemIndicator />
</Menubar.CheckboxItem>
<Menubar.RadioGroup>
<Menubar.RadioItem>
<Menubar.ItemIndicator />
</Menubar.RadioItem>
</Menubar.RadioGroup>
<Menubar.Sub>
<Menubar.SubTrigger />
<Menubar.Portal>
<Menubar.SubContent />
</Menubar.Portal>
</Menubar.Sub>
<Menubar.Separator />
<Menubar.Arrow />
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);
Contains all the parts of a menubar.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
defaultValue | string | |
value | string | |
onValueChange | function | |
dir | enum | |
loop | boolean | false |
A top level menu item, contains a trigger with content combination.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
value | string |
The button that toggles the content. By default, the Menubar.Content
will position itself against the trigger.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
Data Attribute | Values |
---|---|
[data-state] | "open" | "closed" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
When used, portals the content part into the body
.
Prop | Type | Default |
---|---|---|
forceMount | boolean | |
container | HTMLElement | document.body |
The component that pops out when a menu is open.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
loop | boolean | false |
onCloseAutoFocus | function | |
onEscapeKeyDown | function | |
onPointerDownOutside | function | |
onFocusOutside | function | |
onInteractOutside | function | |
forceMount | boolean | |
side | enum | "bottom" |
sideOffset | number | 0 |
align | enum | "center" |
alignOffset | number | 0 |
avoidCollisions | boolean | true |
collisionBoundary | Boundary | [] |
collisionPadding | number | Padding | 0 |
arrowPadding | number | 0 |
sticky | enum | "partial" |
hideWhenDetached | boolean | false |
Data Attribute | Values |
---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
CSS Variable | Description |
---|---|
--radix-menubar-content-transform-origin | The transform-origin computed from the content and arrow positions/offsets |
--radix-menubar-content-available-width | The remaining width between the trigger and the boundary edge |
--radix-menubar-content-available-height | The remaining height between the trigger and the boundary edge |
--radix-menubar-trigger-width | The width of the trigger |
--radix-menubar-trigger-height | The height of the trigger |
An optional arrow element to render alongside a menubar menu. This can be used to help visually link the trigger with the Menubar.Content
. Must be rendered inside Menubar.Content
.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
width | number | 10 |
height | number | 5 |
The component that contains the menubar items.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
disabled | boolean | |
onSelect | function | |
textValue | string |
Data Attribute | Values |
---|---|
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
Used to group multiple Menubar.Item
s.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
Used to render a label. It won't be focusable using arrow keys.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
An item that can be controlled and rendered like a checkbox.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
checked | boolean | 'indeterminate' | |
onCheckedChange | function | |
disabled | boolean | |
onSelect | function | |
textValue | string |
Data Attribute | Values |
---|---|
[data-state] | "checked" | "unchecked" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
Used to group multiple Menubar.RadioItem
s.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
value | string | |
onValueChange | function |
An item that can be controlled and rendered like a radio.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
value* | string | |
disabled | boolean | |
onSelect | function | |
textValue | string |
Data Attribute | Values |
---|---|
[data-state] | "checked" | "unchecked" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
Renders when the parent Menubar.CheckboxItem
or Menubar.RadioItem
is checked. You can style this element directly, or you can use it as a wrapper to put an icon into, or both.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
forceMount | boolean |
Data Attribute | Values |
---|---|
[data-state] | "checked" | "unchecked" |
Used to visually separate items in a menubar menu.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
Contains all the parts of a submenu.
Prop | Type | Default |
---|---|---|
defaultOpen | boolean | |
open | boolean | |
onOpenChange | function |
An item that opens a submenu. Must be rendered inside Menubar.Sub
.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
disabled | boolean | |
textValue | string |
Data Attribute | Values |
---|---|
[data-state] | "open" | "closed" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
The component that pops out when a submenu is open. Must be rendered inside Menubar.Sub
.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
loop | boolean | false |
onEscapeKeyDown | function | |
onPointerDownOutside | function | |
onFocusOutside | function | |
onInteractOutside | function | |
forceMount | boolean | |
sideOffset | number | 0 |
alignOffset | number | 0 |
avoidCollisions | boolean | true |
collisionBoundary | Boundary | [] |
collisionPadding | number | Padding | 0 |
arrowPadding | number | 0 |
sticky | enum | "partial" |
hideWhenDetached | boolean | false |
Data Attribute | Values |
---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
[data-orientation] | "vertical" | "horizontal" |
CSS Variable | Description |
---|---|
--radix-menubar-content-transform-origin | The transform-origin computed from the content and arrow positions/offsets |
--radix-menubar-content-available-width | The remaining width between the trigger and the boundary edge |
--radix-menubar-content-available-height | The remaining height between the trigger and the boundary edge |
--radix-menubar-trigger-width | The width of the trigger |
--radix-menubar-trigger-height | The height of the trigger |
You can create submenus by using Menubar.Sub
in combination with its parts.
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger>…</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.Item>…</Menubar.Item>
<Menubar.Item>…</Menubar.Item>
<Menubar.Separator />
<Menubar.Sub>
<Menubar.SubTrigger>Sub menu →</Menubar.SubTrigger>
<Menubar.Portal>
<Menubar.SubContent>
<Menubar.Item>Sub menu item</Menubar.Item>
<Menubar.Item>Sub menu item</Menubar.Item>
<Menubar.Arrow />
</Menubar.SubContent>
</Menubar.Portal>
</Menubar.Sub>
<Menubar.Separator />
<Menubar.Item>…</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
You can add special styles to disabled items via the data-disabled
attribute.
// index.jsx
import * as Menubar from '@radix-ui/react-menubar';
import './styles.css';
export default () => (
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger>…</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.Item className="MenubarItem" disabled>
…
</Menubar.Item>
<Menubar.Item className="MenubarItem">…</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);
/* styles.css */
.MenubarItem[data-disabled] {
color: gainsboro;
}
Use the Separator
part to add a separator between items.
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger>…</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.Item>…</Menubar.Item>
<Menubar.Separator />
<Menubar.Item>…</Menubar.Item>
<Menubar.Separator />
<Menubar.Item>…</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
Use the Label
part to help label a section.
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger>…</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.Label>Label</Menubar.Label>
<Menubar.Item>…</Menubar.Item>
<Menubar.Item>…</Menubar.Item>
<Menubar.Item>…</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
Use the CheckboxItem
part to add an item that can be checked.
import React from 'react';
import { CheckIcon } from '@radix-ui/react-icons';
import * as Menubar from '@radix-ui/react-menubar';
export default () => {
const [checked, setChecked] = React.useState(true);
return (
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger>…</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.Item>…</Menubar.Item>
<Menubar.Item>…</Menubar.Item>
<Menubar.Separator />
<Menubar.CheckboxItem
checked={checked}
onCheckedChange={setChecked}
>
<Menubar.ItemIndicator>
<CheckIcon />
</Menubar.ItemIndicator>
Checkbox item
</Menubar.CheckboxItem>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);
};
Use the RadioGroup
and RadioItem
parts to add an item that can be checked amongst others.
import React from 'react';
import { CheckIcon } from '@radix-ui/react-icons';
import * as Menubar from '@radix-ui/react-menubar';
export default () => {
const [color, setColor] = React.useState('blue');
return (
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger>…</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.RadioGroup value={color} onValueChange={setColor}>
<Menubar.RadioItem value="red">
<Menubar.ItemIndicator>
<CheckIcon />
</Menubar.ItemIndicator>
Red
</Menubar.RadioItem>
<Menubar.RadioItem value="blue">
<Menubar.ItemIndicator>
<CheckIcon />
</Menubar.ItemIndicator>
Blue
</Menubar.RadioItem>
</Menubar.RadioGroup>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);
};
You can add extra decorative elements in the Item
parts, such as images.
import * as Menubar from '@radix-ui/react-menubar';
export default () => (
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger>…</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content>
<Menubar.Item>
<img src="…" />
Adolfo Hess
</Menubar.Item>
<Menubar.Item>
<img src="…" />
Miyah Myles
</Menubar.Item>
</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);
You may want to constrain the width of the content (or sub-content) so that it matches the trigger (or sub-trigger) width. You may also want to constrain its height to not exceed the viewport.
We expose several CSS custom properties such as --radix-menubar-trigger-width
and --radix-menubar-content-available-height
to support this. Use them to constrain the content dimensions.
// index.jsx
import * as Menubar from '@radix-ui/react-menubar';
import './styles.css';
export default () => (
<Menubar.Root>
<Menubar.Trigger>…</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content className="MenubarContent" sideOffset={5}>
…
</Menubar.Content>
</Menubar.Portal>
</Menubar.Root>
);
/* styles.css */
.MenubarContent {
width: var(--radix-menubar-trigger-width);
max-height: var(--radix-menubar-content-available-height);
}
We expose a CSS custom property --radix-menubar-content-transform-origin
. Use it to animate the content from its computed origin based on side
, sideOffset
, align
, alignOffset
and any collisions.
// index.jsx
import * as Menubar from '@radix-ui/react-menubar';
import './styles.css';
export default () => (
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger>…</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content className="MenubarContent">…</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);
/* styles.css */
.MenubarContent {
transform-origin: var(--radix-menubar-content-transform-origin);
animation: scaleIn 0.5s ease-out;
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0);
}
to {
opacity: 1;
transform: scale(1);
}
}
We expose data-side
and data-align
attributes. Their values will change at runtime to reflect collisions. Use them to create collision and direction-aware animations.
// index.jsx
import * as Menubar from '@radix-ui/react-menubar';
import './styles.css';
export default () => (
<Menubar.Root>
<Menubar.Menu>
<Menubar.Trigger>…</Menubar.Trigger>
<Menubar.Portal>
<Menubar.Content className="MenubarContent">…</Menubar.Content>
</Menubar.Portal>
</Menubar.Menu>
</Menubar.Root>
);
/* styles.css */
.MenubarContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.MenubarContent[data-side='top'] {
animation-name: slideUp;
}
.MenubarContent[data-side='bottom'] {
animation-name: slideDown;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Adheres to the Menu Button WAI-ARIA design pattern and uses roving tabindex to manage focus movement among menu items.
Key | Description |
---|---|
Space | When focus is on Menubar.Trigger , opens the menubar and focuses the first item.When focus is on an item, activates the focused item. |
Enter | When focus is on Menubar.Trigger , opens the associated menu.When focus is on an item, activates the focused item. |
ArrowDown | When focus is on Menubar.Trigger , opens the associated menu.When focus is on an item, moves focus to the next item. |
ArrowUp | When focus is on an item, moves focus to the previous item. |
ArrowRightArrowLeft | When focus is on a Menubar.Trigger , moves focus to the next or previous item. When focus is on a Menubar.SubTrigger , opens or closes the submenu depending on reading direction. When focus is within a Menubar.Content , opens the next menu in the menubar. |
Esc | Closes the currently open menu and moves focus to its Menubar.Trigger . |