Menu
Menus display a list of actions or options that a user can choose.
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 {Menu, MenuTrigger, MenuItem, ActionButton} from '@react-spectrum/s2';
let items = [
{id: 'cut', name: 'Cut'},
{id: 'copy', name: 'Copy'},
{id: 'paste', name: 'Paste'},
{id: 'select-all', name: 'Select All'}
];
<MenuTrigger>
<ActionButton>Edit</ActionButton>
<Menu items={items}>
{item => <MenuItem>{item.name}</MenuItem>}
</Menu>
</MenuTrigger>
Slots
MenuItem
supports icons or images, label
and description
text slots, and keyboard shortcuts.
import {MenuTrigger, ActionButton, Menu, MenuItem, Text, Keyboard} from '@react-spectrum/s2';
import Copy from '@react-spectrum/s2/icons/Copy';
import Cut from '@react-spectrum/s2/icons/Cut';
import Paste from '@react-spectrum/s2/icons/Paste';
<MenuTrigger>
<ActionButton>Edit</ActionButton>
<Menu>
<MenuItem textValue="Copy">
<Copy />
<Text slot="label">Copy</Text>
<Text slot="description">Copy the selected text</Text>
<Keyboard>⌘C</Keyboard>
</MenuItem>
<MenuItem textValue="Cut">
<Cut />
<Text slot="label">Cut</Text>
<Text slot="description">Cut the selected text</Text>
<Keyboard>⌘X</Keyboard>
</MenuItem>
<MenuItem textValue="Paste">
<Paste />
<Text slot="label">Paste</Text>
<Text slot="description">Paste the copied text</Text>
<Keyboard>⌘V</Keyboard>
</MenuItem>
</Menu>
</MenuTrigger>
Sections
Use the <MenuSection>
component to group options. A <Header>
element, with a <Heading>
and optional description
slot can be included to label a section. Sections without a header must have an aria-label
.
import {MenuTrigger, ActionButton, Menu, MenuItem, MenuSection, Header, Heading, Text} from '@react-spectrum/s2';
<MenuTrigger>
<ActionButton>Publish</ActionButton>
<Menu>
<MenuSection>
<Header>
<Heading>Export</Heading>
<Text slot="description">Save to your device</Text>
</Header>
<MenuItem>Image…</MenuItem>
<MenuItem>Video…</MenuItem>
<MenuItem>Text…</MenuItem>
</MenuSection>
<MenuSection>
<Header>
<Heading>Share</Heading>
<Text slot="description">Share to social media</Text>
</Header>
<MenuItem>YouTube…</MenuItem>
<MenuItem>Instagram…</MenuItem>
<MenuItem>Email…</MenuItem>
</MenuSection>
</Menu>
</MenuTrigger>
Submenus
Wrap a <MenuItem>
and a <Menu>
with a <SubmenuTrigger>
to create a submenu.
import {Menu, MenuTrigger, MenuItem, SubmenuTrigger, Popover, ActionButton} from '@react-spectrum/s2';
<MenuTrigger>
<ActionButton>Actions</ActionButton>
<Menu>
<MenuItem>Copy</MenuItem>
<MenuItem>Cut</MenuItem>
<MenuItem>Paste</MenuItem>
<SubmenuTrigger>
<MenuItem id="share">Share</MenuItem>
<Popover>
<Menu>
<MenuItem>Email</MenuItem>
<MenuItem>SMS</MenuItem>
<MenuItem>Copy Link</MenuItem>
</Menu>
</Popover>
</SubmenuTrigger>
</Menu>
</MenuTrigger>
Links
Use the href
prop on a <MenuItem>
to create a link. See the client side routing guide to learn how to integrate with your framework.
Popover content
Wrap the Menu in a Popover to add additional sibling elements. This example includes a header with interactive content.
import {Popover, MenuTrigger, ActionButton, SearchField, Menu, MenuItem, MenuSection, SubmenuTrigger, Avatar, Switch, Divider, Text} from '@react-spectrum/s2';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
import Buildings from '@react-spectrum/s2/icons/Buildings';
import Settings from '@react-spectrum/s2/icons/Settings';
<MenuTrigger>
<ActionButton aria-label="Account">
<Avatar src="https://i.imgur.com/xIe7Wlb.png" />
</ActionButton>
<Popover>
<div className={style({paddingTop: 4, display: 'flex', flexDirection: 'column', gap: 12})}>
<div className={style({display: 'flex', gap: 12, alignItems: 'center', marginX: 12})}>
<Avatar src="https://i.imgur.com/xIe7Wlb.png" size={56} />
<div>
<div className={style({font: 'title'})}>Devon Govett</div>
<div className={style({font: 'ui'})}>user@example.com</div>
<Switch styles={style({marginTop: 4})}>Dark theme</Switch>
</div>
</div>
<Divider styles={style({marginX: 12})} />
<Menu aria-label="Account">
<MenuSection>
<SubmenuTrigger>
<MenuItem>
<Buildings />
<Text slot="label">Organization</Text>
<Text slot="value">Nike</Text>
</MenuItem>
<Menu selectionMode="single" selectedKeys={['nike']}>
<MenuItem id="adobe">Adobe</MenuItem>
<MenuItem id="nike">Nike</MenuItem>
<MenuItem id="apple">Apple</MenuItem>
</Menu>
</SubmenuTrigger>
<MenuItem>
<Settings />
<Text slot="label">Settings</Text>
</MenuItem>
</MenuSection>
<MenuSection>
<MenuItem>Legal notices</MenuItem>
<MenuItem>Sign out</MenuItem>
</MenuSection>
</Menu>
</div>
</Popover>
</MenuTrigger>
Autocomplete
Use Autocomplete from React Aria Components with a SearchField to make a menu searchable.
import {Autocomplete, MenuTrigger, ActionButton, Menu, MenuItem, Popover, SearchField} from '@react-spectrum/s2';
import {useFilter} from 'react-aria-components';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
function Example() {
let {contains} = useFilter({sensitivity: 'base'});
return (
<MenuTrigger>
<ActionButton>Add tag...</ActionButton>
<Popover aria-label="Select a tag">
<Autocomplete filter={contains}>
<SearchField aria-label="Search tags" autoFocus />
<Menu styles={style({marginTop: 8})}>
<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>
</Popover>
</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 {Menu, MenuTrigger, MenuItem, MenuSection, Header, Heading, ActionButton, Selection} from '@react-spectrum/s2';
import {useState} from 'react';
function Example() {
let [style, setStyle] = useState<Selection>(new Set(['bold']));
let [align, setAlign] = useState<Selection>(new Set(['left']));
return (
<MenuTrigger>
<ActionButton>Edit</ActionButton>
<Menu>
<MenuSection>
<Header>
<Heading>Clipboard</Heading>
</Header>
<MenuItem>Cut</MenuItem>
<MenuItem>Copy</MenuItem>
<MenuItem>Paste</MenuItem>
</MenuSection>
<MenuSection
selectionMode="multiple"
selectedKeys={style}
onSelectionChange={setStyle}>
<Header>
<Heading>Text style</Heading>
</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>
<Heading>Text alignment</Heading>
</Header>
<MenuItem id="left">Left</MenuItem>
<MenuItem id="center">Center</MenuItem>
<MenuItem id="right">Right</MenuItem>
</MenuSection>
</Menu>
</MenuTrigger>
);
}
Menu trigger
MenuTrigger accepts props to control trigger interactions and Menu positioning.
direction
API
<MenuTrigger>
<Button />
<Menu>
<MenuItem>
<Icon /> or <Image />
<Text slot="label" />
<Text slot="description" />
<Keyboard />
</MenuItem>
<MenuSection>
<Header>
<Heading />
<Text slot="description" />
</Header>
<MenuItem />
</MenuSection>
<SubmenuTrigger>
<MenuItem />
<Menu />
</SubmenuTrigger>
</Menu>
</MenuTrigger>
MenuTrigger
The MenuTrigger serves as a wrapper around a Menu and its associated trigger, linking the Menu's open state with the trigger's press state.
Name | Type | Default |
---|---|---|
children | ReactNode | Default: — |
trigger | MenuTriggerType | Default: 'press'
|
How the menu is triggered. | ||
Menu
Menus display a list of actions or options that a user can choose.
Name | Type | Default |
---|---|---|
size | 'S'
| 'M'
| 'L'
| 'XL' | Default: 'M'
|
The size of the Menu. | ||
hideLinkOutIcon | boolean | Default: — |
Hides the default link out icons on menu items that open links in a new tab. | ||
styles | StylesProp | Default: — |
Spectrum-defined styles, returned by the style() macro. | ||
children | ReactNode | | Default: — |
The contents of the collection. | ||
items | Iterable | Default: — |
Item objects in the collection. | ||
selectionMode | SelectionMode | Default: — |
The type of selection that is allowed in the collection. | ||
selectedKeys | 'all' | Iterable | Default: — |
The currently selected keys in the collection (controlled). | ||
defaultSelectedKeys | 'all' | Iterable | Default: — |
The initial selected keys in the collection (uncontrolled). | ||
onSelectionChange |
| Default: — |
Handler that is called when the selection changes. | ||
disabledKeys | Iterable | Default: — |
The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with. | ||
disallowEmptySelection | boolean | Default: — |
Whether the collection allows empty selection. | ||
shouldFocusWrap | boolean | Default: — |
Whether keyboard navigation is circular. | ||
escapeKeyBehavior | 'clearSelection' | 'none' | Default: 'clearSelection'
|
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. | ||
MenuItem
Name | Type | |
---|---|---|
children | ReactNode | |
The contents of the item. | ||
id | Key | |
The unique id of the item. | ||
value | T | |
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. | ||
styles | StylesProp | |
Spectrum-defined styles, returned by the style() macro. | ||
MenuSection
Name | Type | |
---|---|---|
id | Key | |
The unique id of the section. | ||
value | T | |
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. | ||
SubmenuTrigger
Name | Type | |
---|---|---|
children | ReactElement | |
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). |