Beta Preview

Menu

Menus display a list of actions or options that a user can choose.

size 
import {Menu, MenuTrigger, MenuItem, MenuSection, SubmenuTrigger, Header, Heading, Text, ActionButton} from '@react-spectrum/s2';
import Image from '@react-spectrum/s2/icons/Image';
import Copy from '@react-spectrum/s2/icons/Copy';
import DeviceTablet from '@react-spectrum/s2/icons/DeviceTablet';
import DeviceDesktop from '@react-spectrum/s2/icons/DeviceDesktop';

function Example(props) {
  return (
    <MenuTrigger>
      <ActionButton>Publish</ActionButton>
      <Menu {...props}>
        <MenuSection>
          <Header>
            <Heading>Publish and export</Heading>
            <Text slot="description">
              Social media, other formats
            </Text>
          </Header>
          <MenuItem
            textValue="quick export"
            onAction={() => alert('Quick export')}>
            <Image />
            <Text slot="label">Quick Export</Text>
            <Text slot="description">
              Share a low-res snapshot.
            </Text>
          </MenuItem>
          <SubmenuTrigger>
            <MenuItem textValue="open a copy">
              <Copy />
              <Text slot="label">Open a copy</Text>
              <Text slot="description">
                Illustrator for iPad or desktop
              </Text>
            </MenuItem>
            <Menu>
              <MenuSection>
                <Header>
                  <Heading>Open a copy in</Heading>
                </Header>
                <MenuItem
                  textValue="ipad"
                  onAction={() => alert('Open on iPad')}>
                  <DeviceTablet />
                  <Text slot="label">Illustrator for iPad</Text>
                </MenuItem>
                <MenuItem
                  textValue="desktop"
                  onAction={() => alert('Open on desktop')}>
                  <DeviceDesktop />
                  <Text slot="label">Illustrator for desktop</Text>
                </MenuItem>
              </MenuSection>
            </Menu>
          </SubmenuTrigger>
        </MenuSection>
        <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 {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>

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>

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.

hideLinkOutIcon 
import {Menu, MenuTrigger, MenuItem, ActionButton} from '@react-spectrum/s2';

function Example(props) {
  return (
    <MenuTrigger>
      <ActionButton>Links</ActionButton>
      <Menu {...props}>
        <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>
  );
}

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

selectionMode 
disallowEmptySelection 
import {Menu, MenuTrigger, MenuItem, ActionButton, Selection} from '@react-spectrum/s2';
import {useState} from 'react';

function Example(props) {
  let [selected, setSelected] = useState<Selection>(new Set(['rulers']));

  return (
    <>
      <MenuTrigger>
        <ActionButton>View</ActionButton>
        <Menu
          {...props}
          selectionMode="multiple"
          selectedKeys={selected}
          onSelectionChange={setSelected}>
          <MenuItem id="grid">Pixel grid</MenuItem>
          <MenuItem id="rulers">Rulers</MenuItem>
          <MenuItem id="comments" isDisabled>Comments</MenuItem>
          <MenuItem id="layout">Layout guides</MenuItem>
          <MenuItem id="toolbar">Toolbar</MenuItem>
        </Menu>
      </MenuTrigger>
      <p>Current selection: {selected === 'all' ? 'all' : [...selected].join(', ')}</p>
    </>
  );
}

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>
  );
}

MenuTrigger accepts props to control trigger interactions and Menu positioning.

trigger 
align 
direction 
shouldFlip 
import {MenuTrigger, Menu, MenuItem, ActionButton} from '@react-spectrum/s2';

<MenuTrigger>
  <ActionButton>Copy</ActionButton>
  <Menu>
    <MenuItem>Copy as plain text</MenuItem>
    <MenuItem>Copy as rich text</MenuItem>
    <MenuItem>Copy URL</MenuItem>
  </Menu>
</MenuTrigger>

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>

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.

NameTypeDefault
childrenReactNodeDefault:
triggerDefault: 'press'
How the menu is triggered.

Menus display a list of actions or options that a user can choose.

NameTypeDefault
size'S''M''L''XL'Default: 'M'
The size of the Menu.
hideLinkOutIconbooleanDefault:
Hides the default link out icons on menu items that open links in a new tab.
stylesDefault:
Spectrum-defined styles, returned by the style() macro.
childrenReactNode(item: object) => ReactNodeDefault:
The contents of the collection.
itemsIterable<T>Default:
Item objects in the collection.
selectionModeDefault:
The type of selection that is allowed in the collection.
selectedKeys'all'Iterable<Key>Default:
The currently selected keys in the collection (controlled).
defaultSelectedKeys'all'Iterable<Key>Default:
The initial selected keys in the collection (uncontrolled).
onSelectionChange(keys: ) => voidDefault:
Handler that is called when the selection changes.
disabledKeysIterable<Key>Default:
The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.
disallowEmptySelectionbooleanDefault:
Whether the collection allows empty selection.
shouldFocusWrapbooleanDefault:
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.
NameType
childrenReactNode
The contents of the item.
idKey
The unique id of the item.
valueT
The object value that this item represents. When using dynamic collections, this is set automatically.
textValuestring
A string representation of the item's contents, used for features like typeahead.
isDisabledboolean
Whether the item is disabled.
styles
Spectrum-defined styles, returned by the style() macro.
NameType
idKey
The unique id of the section.
valueT
The object value that this section represents. When using dynamic collections, this is set automatically.
childrenReactNode(item: T) => ReactElement
Static child items or a function to render children.
itemsIterable<T>
Item objects in the section.
dependenciesReadonlyArray<any>
Values that should invalidate the item cache when using dynamic collections.
selectionMode
The type of selection that is allowed in the collection.
selectedKeys'all'Iterable<Key>
The currently selected keys in the collection (controlled).
defaultSelectedKeys'all'Iterable<Key>
The initial selected keys in the collection (uncontrolled).
onSelectionChange(keys: ) => void
Handler that is called when the selection changes.
disabledKeysIterable<Key>
The currently disabled keys in the collection (controlled).
disallowEmptySelectionboolean
Whether the collection allows empty selection.
NameType
childrenReactElement[]
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).