A menu displays a list of actions or options that a user can choose.
Vanilla CSS theme
--tint CSS variable used by the Vanilla CSS examples.import {MenuTrigger, SubmenuTrigger, Menu, MenuItem, MenuSection} from './Menu';
import {Separator, Text, Keyboard} from 'react-aria-components';
import {Button} from './Button';
import {Ellipsis, FolderOpen, Pencil, Copy, Trash, Share, Mail, Smartphone, Instagram} from 'lucide-react';
<MenuTrigger>
<Button aria-label="Actions">
<Ellipsis size={18} />
</Button>
<Menu>
<MenuSection>
<MenuItem onAction={() => alert('open')}>
<FolderOpen />
<Text slot="label">Open</Text>
<Keyboard>⌘O</Keyboard>
</MenuItem>
<MenuItem onAction={() => alert('rename')}>
<Pencil />
<Text slot="label">Rename…</Text>
<Keyboard>⌘R</Keyboard>
</MenuItem>
<MenuItem onAction={() => alert('duplicate')}>
<Copy />
<Text slot="label">Duplicate</Text>
<Keyboard>⌘D</Keyboard>
</MenuItem>
<MenuItem onAction={() => alert('delete')}>
<Trash />
<Text slot="label">Delete…</Text>
<Keyboard>⌘⌫</Keyboard>
</MenuItem>
<SubmenuTrigger>
<MenuItem>
<Share />
<Text slot="label">Share</Text>
</MenuItem>
<Menu>
<MenuItem>
<Mail />
<Text slot="label">Email</Text>
</MenuItem>
<MenuItem>
<Smartphone />
<Text slot="label">SMS</Text>
</MenuItem>
<MenuItem>
<Instagram />
<Text slot="label">Instagram</Text>
</MenuItem>
</Menu>
</SubmenuTrigger>
</MenuSection>
<Separator />
<MenuSection selectionMode="multiple" defaultSelectedKeys={['files']}>
<MenuItem id="files">Show files</MenuItem>
<MenuItem id="folders">Show folders</MenuItem>
</MenuSection>
</Menu>
</MenuTrigger>
Content
Menu follows the Collection Components API, accepting both static and dynamic collections. This example shows a dynamic collection, passing a list of objects to the items prop, and a function to render the children.
import {MenuTrigger, Menu, MenuItem} from './Menu';
import {Button} from './Button';
function Example() {
let items = [
{ id: 1, name: 'New file…' },
{ id: 2, name: 'New window' },
{ id: 3, name: 'Open…' },
{ id: 4, name: 'Save' },
{ id: 5, name: 'Save as…' },
{ id: 6, name: 'Revert file' },
{ id: 7, name: 'Print…' },
{ id: 8, name: 'Close window' },
{ id: 9, name: 'Quit' }
];
return (
<MenuTrigger>
<Button>File</Button>
<Menu items={items}>
{(item) => <MenuItem>{item.name}</MenuItem>}
</Menu>
</MenuTrigger>
);
}
Text slots
Use the "label" and "description" slots to separate primary and secondary content within a <MenuItem>. This improves screen reader announcements and can also be used for styling purposes. Use the <Keyboard> component to display a keyboard shortcut.
import {MenuTrigger, Menu, MenuItem} from './Menu';
import {Text, Keyboard} from 'react-aria-components';
import {Button} from './Button';
<MenuTrigger>
<Button>Permissions</Button>
<Menu>
<MenuItem textValue="Copy">
<Text slot="label">Copy</Text>
<Text slot="description">Copy the selected text</Text>
<Keyboard>⌘C</Keyboard>
</MenuItem>
<MenuItem textValue="Cut">
<Text slot="label">Cut</Text>
<Text slot="description">Cut the selected text</Text>
<Keyboard>⌘X</Keyboard>
</MenuItem>
<MenuItem textValue="Paste">
<Text slot="label">Paste</Text>
<Text slot="description">Paste the copied text</Text>
<Keyboard>⌘V</Keyboard>
</MenuItem>
</Menu>
</MenuTrigger>
Accessibility
Sections
Use the <MenuSection> component to group options. A <Header> element may also be included to label the section. Sections without a header must have an aria-label.
import {Header} from 'react-aria-components';
import {MenuTrigger, Menu, MenuItem, MenuSection} from './Menu';
import {Button} from './Button';
<MenuTrigger>
<Button>Publish</Button>
<Menu>
<MenuSection>
<Header>Export</Header>
<MenuItem>Image…</MenuItem>
<MenuItem>Video…</MenuItem>
<MenuItem>Text…</MenuItem>
</MenuSection>
<MenuSection>
<Header>Share</Header>
<MenuItem>YouTube…</MenuItem>
<MenuItem>Instagram…</MenuItem>
<MenuItem>Email…</MenuItem>
</MenuSection>
</Menu>
</MenuTrigger>
Submenus
Wrap a <MenuItem> with a <SubmenuTrigger> to create a submenu.
import {MenuTrigger, SubmenuTrigger, Menu, MenuItem} from './Menu';
import {Button} from './Button';
<MenuTrigger>
<Button>Actions</Button>
<Menu>
<MenuItem>Cut</MenuItem>
<MenuItem>Copy</MenuItem>
<MenuItem>Delete</MenuItem>
<SubmenuTrigger>
<MenuItem>Share</MenuItem>
<Menu>
<MenuItem>SMS</MenuItem>
<MenuItem>Instagram</MenuItem>
<SubmenuTrigger>
<MenuItem>Email</MenuItem>
<Menu>
<MenuItem>Work</MenuItem>
<MenuItem>Personal</MenuItem>
</Menu>
</SubmenuTrigger>
</Menu>
</SubmenuTrigger>
</Menu>
</MenuTrigger>
Separators
Separators may be added between menu items or sections in order to create non-labeled groupings.
import {MenuTrigger, Menu, MenuItem} from './Menu';
import {Separator} from 'react-aria-components';
import {Button} from './Button';
<MenuTrigger>
<Button>Actions</Button>
<Menu>
<MenuItem>New…</MenuItem>
<MenuItem>Open…</MenuItem>
<Separator />
<MenuItem>Save</MenuItem>
<MenuItem>Save as…</MenuItem>
<MenuItem>Rename…</MenuItem>
<Separator />
<MenuItem>Page setup…</MenuItem>
<MenuItem>Print…</MenuItem>
</Menu>
</MenuTrigger>
Links
Use the href prop on a <MenuItem> to create a link. See the framework setup guide to learn how to integrate with your framework.
import {MenuTrigger, Menu, MenuItem} from './Menu';
import {Button} from './Button';
<MenuTrigger>
<Button>Links</Button>
<Menu>
<MenuItem href="https://adobe.com/" target="_blank">Adobe</MenuItem>
<MenuItem href="https://apple.com/" target="_blank">Apple</MenuItem>
<MenuItem href="https://google.com/" target="_blank">Google</MenuItem>
<MenuItem href="https://microsoft.com/" target="_blank">Microsoft</MenuItem>
</Menu>
</MenuTrigger>
Autocomplete
Popovers can include additional components as siblings of a menu. This example uses an Autocomplete with a SearchField to let the user filter the items.
import {Autocomplete, useFilter} from 'react-aria-components';
import {MenuTrigger, Menu, MenuItem} from './Menu';
import {Button} from './Button';
import {SearchField} from './SearchField';
function Example() {
let {contains} = useFilter({sensitivity: 'base'});
return (
<MenuTrigger>
<Button>Add tag...</Button>
<div style={{display: 'flex', flexDirection: 'column', maxHeight: 'inherit'}}>
<Autocomplete filter={contains}>
<SearchField aria-label="Search tags" placeholder="Search tags" autoFocus style={{margin: 4}} />
<Menu style={{flex: 1}}>
<MenuItem>News</MenuItem>
<MenuItem>Travel</MenuItem>
<MenuItem>Shopping</MenuItem>
<MenuItem>Business</MenuItem>
<MenuItem>Entertainment</MenuItem>
<MenuItem>Food</MenuItem>
<MenuItem>Technology</MenuItem>
<MenuItem>Health</MenuItem>
<MenuItem>Science</MenuItem>
</Menu>
</Autocomplete>
</div>
</MenuTrigger>
);
}
Selection
Use the selectionMode prop to enable single or multiple selection. The selected items can be controlled via the selectedKeys prop, matching the id prop of the items. Items can be disabled with the isDisabled prop. See the selection guide for more details.
Current selection: rulers
Section-level selection
Each section in a menu may have independent selection states by passing selectionMode and selectedKeys to the MenuSection.
import type {Selection} from 'react-aria-components';
import {Header} from 'react-aria-components';
import {MenuTrigger, Menu, MenuItem, MenuSection} from './Menu';
import {Button} from './Button';
import {useState} from 'react';
function Example() {
let [style, setStyle] = useState<Selection>(new Set(['bold']));
let [align, setAlign] = useState<Selection>(new Set(['left']));
return (
<MenuTrigger>
<Button>Edit</Button>
<Menu>
<MenuSection>
<Header>Clipboard</Header>
<MenuItem>Cut</MenuItem>
<MenuItem>Copy</MenuItem>
<MenuItem>Paste</MenuItem>
</MenuSection>
<MenuSection
selectionMode="multiple"
selectedKeys={style}
onSelectionChange={setStyle}>
<Header>Text style</Header>
<MenuItem id="bold">Bold</MenuItem>
<MenuItem id="italic">Italic</MenuItem>
<MenuItem id="underline">Underline</MenuItem>
</MenuSection>
<MenuSection selectionMode="single" selectedKeys={align} onSelectionChange={setAlign}>
<Header>Text alignment</Header>
<MenuItem id="left">Left</MenuItem>
<MenuItem id="center">Center</MenuItem>
<MenuItem id="right">Right</MenuItem>
</MenuSection>
</Menu>
</MenuTrigger>
);
}
Menu trigger
Custom trigger
MenuTrigger works with any pressable React Aria component (e.g. Button, Link, etc.). Use the <Pressable> component or usePress hook to wrap a custom trigger element such as a third party component or DOM element.
import {Pressable} from 'react-aria-components';
import {MenuTrigger, Menu, MenuItem} from './Menu';
<MenuTrigger>
<Pressable>
<span role="button">Custom trigger</span>
</Pressable>
<Menu>
<MenuItem>Open</MenuItem>
<MenuItem>Rename…</MenuItem>
<MenuItem>Duplicate</MenuItem>
<MenuItem>Delete…</MenuItem>
</Menu>
</MenuTrigger>
Accessibility
<Pressable> child must have an interactive ARIA role or use an appropriate semantic HTML element so that screen readers can announce the trigger. Trigger components must forward their ref and spread all props to a DOM element.const CustomTrigger = React.forwardRef((props, ref) => (
<button {...props} ref={ref} />
));
Long press
Use trigger="longPress" to open the menu on long press instead of on click/tap. Keyboard users can open the menu using Alt ▼. This is useful when the menu trigger has a primary action on press, and the menu provides secondary actions.
import {MenuTrigger, Menu, MenuItem} from './Menu';
import {Button} from './Button';
import {ChevronDown} from 'lucide-react';
<MenuTrigger trigger="longPress">
<Button onPress={() => alert('crop')}>
<span>Crop</span>
<ChevronDown size={18} />
</Button>
<Menu>
<MenuItem>Rotate</MenuItem>
<MenuItem>Slice</MenuItem>
<MenuItem>Clone stamp</MenuItem>
</Menu>
</MenuTrigger>
Examples
API
<MenuTrigger>
<Button />
<Popover>
<Menu>
<MenuItem>
<Text slot="label" />
<Text slot="description" />
<Keyboard />
<SelectionIndicator />
</MenuItem>
<Separator />
<MenuSection>
<Header />
<MenuItem />
</MenuSection>
<SubmenuTrigger>
<MenuItem />
<Popover>
<Menu />
</Popover>
</SubmenuTrigger>
</Menu>
</Popover>
</MenuTrigger>
MenuTrigger
| Name | Type | Default |
|---|---|---|
children | ReactNode | Default: — |
trigger | MenuTriggerType | Default: 'press'
|
| How the menu is triggered. | ||
Menu
A menu displays a list of actions or options that a user can choose.
| Name | Type | |
|---|---|---|
children | ReactNode | | |
| The contents of the collection. | ||
items | Iterable | |
| Item objects in the collection. | ||
renderEmptyState | | |
| Provides content to display when there are no items in the list. | ||
dependencies | ReadonlyArray | |
| Values that should invalidate the item cache when using dynamic collections. | ||
selectionMode | SelectionMode | |
| The type of selection that is allowed in the collection. | ||
selectedKeys | 'all' | Iterable | |
| The currently selected keys in the collection (controlled). | ||
defaultSelectedKeys | 'all' | Iterable | |
| The initial selected keys in the collection (uncontrolled). | ||
onSelectionChange | | |
| Handler that is called when the selection changes. | ||
disabledKeys | Iterable | |
| The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with. | ||
disallowEmptySelection | boolean | |
| Whether the collection allows empty selection. | ||
shouldFocusWrap | boolean | |
| Whether keyboard navigation is circular. | ||
escapeKeyBehavior | 'clearSelection' | 'none' | |
| Whether pressing the escape key should clear selection in the menu or not. Most experiences should not modify this option as it eliminates a keyboard user's ability to easily clear selection. Only use if the escape key is being handled externally or should not trigger selection clearing contextually. | ||
Default className: react-aria-Menu
| Render Prop | CSS Selector |
|---|---|
isEmpty | CSS Selector: [data-empty]
|
| Whether the menu has no items and should display its empty state. | |
MenuItem
A MenuItem represents an individual action in a Menu.
| Name | Type | |
|---|---|---|
id | Key | |
| The unique id of the item. | ||
value | object | |
| The object value that this item represents. When using dynamic collections, this is set automatically. | ||
textValue | string | |
| A string representation of the item's contents, used for features like typeahead. | ||
isDisabled | boolean | |
| Whether the item is disabled. | ||
children | ChildrenOrFunction | |
| The children of the component. A function may be provided to alter the children based on component state. | ||
Default className: react-aria-MenuItem
| Render Prop | CSS Selector |
|---|---|
hasSubmenu | CSS Selector: [data-has-submenu]
|
| Whether the item has a submenu. | |
isOpen | CSS Selector: [data-open]
|
| Whether the item's submenu is open. | |
isHovered | CSS Selector: [data-hovered]
|
| Whether the item is currently hovered with a mouse. | |
isPressed | CSS Selector: [data-pressed]
|
| Whether the item is currently in a pressed state. | |
isSelected | CSS Selector: [data-selected]
|
| Whether the item is currently selected. | |
isFocused | CSS Selector: [data-focused]
|
| Whether the item is currently focused. | |
isFocusVisible | CSS Selector: [data-focus-visible]
|
| Whether the item is currently keyboard focused. | |
isDisabled | CSS Selector: [data-disabled]
|
Whether the item is non-interactive, i.e. both selection and actions are disabled and the item may
not be focused. Dependent on disabledKeys and disabledBehavior. | |
selectionMode | CSS Selector: [data-selection-mode="single | multiple"]
|
| The type of selection that is allowed in the collection. | |
selectionBehavior | CSS Selector: — |
| The selection behavior for the collection. | |
MenuSection
A MenuSection represents a section within a Menu.
| Name | Type | |
|---|---|---|
id | Key | |
| The unique id of the section. | ||
value | object | |
| The object value that this section represents. When using dynamic collections, this is set automatically. | ||
children | ReactNode | | |
| Static child items or a function to render children. | ||
items | Iterable | |
| Item objects in the section. | ||
dependencies | ReadonlyArray | |
| Values that should invalidate the item cache when using dynamic collections. | ||
selectionMode | SelectionMode | |
| The type of selection that is allowed in the collection. | ||
selectedKeys | 'all' | Iterable | |
| The currently selected keys in the collection (controlled). | ||
defaultSelectedKeys | 'all' | Iterable | |
| The initial selected keys in the collection (uncontrolled). | ||
onSelectionChange | | |
| Handler that is called when the selection changes. | ||
disabledKeys | Iterable | |
| The currently disabled keys in the collection (controlled). | ||
disallowEmptySelection | boolean | |
| Whether the collection allows empty selection. | ||
Default className: react-aria-MenuSection
SubmenuTrigger
A submenu trigger is used to wrap a submenu's trigger item and the submenu itself.
| Name | Type | Default |
|---|---|---|
children | ReactElement | Default: — |
| The contents of the SubmenuTrigger. The first child should be an Item (the trigger) and the second child should be the Popover (for the submenu). | ||
delay | number | Default: 200
|
| The delay time in milliseconds for the submenu to appear after hovering over the trigger. | ||