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.

installyarn add @react-spectrum/menu
version3.0.0-alpha.1
usageimport {MenuTrigger} from '@react-spectrum/menu'

Example#


<MenuTrigger>
  <ActionButton>
      Menu Button
  </ActionButton>
  <Menu items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]} itemKey="name">
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>

Content#


The MenuTrigger accepts exactly two children: the Menu and the element which triggers the opening of the Menu. The trigger must be the first child passed into the MenuTrigger and should be an element that supports press events.

If the Menu is open it will close on blur or scroll events.

The defaultSelectedKeys prop can be used to preselect items in the Menu placing it in an uncontrolled state. Alternatively, the selectedKeys preselect items in the Menu placing it in a controlled state.

<MenuTrigger closeOnSelect={false}>
  <ActionButton>
      Menu Controlled
  </ActionButton>
  <Menu
    items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]}
    itemKey="name"
    selectionMode="single"
    selectedKeys={['Kangaroo']}>
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>
<MenuTrigger closeOnSelect={false}>
  <ActionButton>
      Menu Uncontrolled
  </ActionButton>
  <Menu
    items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]}
    itemKey="name"
    selectionMode="single"
    defaultSelectedKeys={['Snake']}>
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>

The selectionMode prop can be used to switch from the default selection of a single menu item to multiple menu items or none.

<MenuTrigger closeOnSelect={false}>
  <ActionButton>
      Menu Selection Multiple
  </ActionButton>
  <Menu
    items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]}
    itemKey="name"
    defaultSelectedKeys={['Kangaroo', 'Snake']}
    selectionMode="multiple">
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>
<MenuTrigger closeOnSelect={false}>
  <ActionButton>
      Menu Selection None
  </ActionButton>
  <Menu
    items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]}
    itemKey="name"
    selectionMode="none">
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>

Sections#

Menus may have Sections with titles and children items.

<MenuTrigger>
  <ActionButton>
      Menu Button
  </ActionButton>
  <Menu items={[{name: 'Section 1', children: [{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]}, {name: 'Section 2', children: [{name: 'Koala'}]}]} itemKey="name">
    {item => (
      <Section items={item.children} title={item.name}>
        {item => <Item>{item.name}</Item>}
      </Section>
    )}
  </Menu>
</MenuTrigger>

Sematic Elements#

A Menu item's content may be any renderable node, not just strings.

View guidelines

import {Keyboard, Text} from '@react-spectrum/typography';

<MenuTrigger>
  <ActionButton>
      Menu Button
  </ActionButton>
  <Menu
    itemKey="name"
    items={[
      {name: 'Copy', icon: 'Copy', shortcut: '⌘C'},
      {name: 'Cut', icon: 'Cut', shortcut: '⌘X'},
      {name: 'Paste', icon: 'Paste', shortcut: '⌘V'}
    ]}>
    {item => {
      let iconMap = {
        Copy,
        Cut,
        Paste
      };
      let Icon = iconMap[item.icon];
      return (
        <Item childItems={item.children} textValue={item.name}>
          <Icon size="S" />
          <Text>{item.name}</Text>
          <Keyboard>{item.shortcut}</Keyboard>
        </Item>
      );
    }}
  </Menu>
</MenuTrigger>

Accessibility#

Menu sections without labels use the prop aria-label.

<MenuTrigger>
  <ActionButton>
      Menu Button
  </ActionButton>
  <Menu items={[{name: 'Section 1', children: [{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]}, {name: 'Section 2', children: [{name: 'Koala'}]}]} itemKey="name">
    {item => (
      <Section items={item.children} aria-label={item.name}>
        {item => <Item>{item.name}</Item>}
      </Section>
    )}
  </Menu>
</MenuTrigger>

Events#


MenuTrigger accepts an onOpenChange handler which is triggered whenever the Menu is opened or closed.

The example below uses onOpenChange to update a separate span element with the current open state of the Menu.

function Example() {
  let [state, setState] = React.useState(false);

  return (
    <div>
      <MenuTrigger onOpenChange={(isOpen) => setState(isOpen)}>
        <ActionButton>
            Menu Button
        </ActionButton>
        <Menu items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]} itemKey="name">
          {item => <Item>{item.name}</Item>}
        </Menu>
      </MenuTrigger>
      <span style={{'margin-left': '8px'}}>Current open state: {state.toString()}</span>
    </div>
  );
}

Menu accepts an onAction handler which is triggered whenever a Menu item is selected.

The example below uses onAction to tell the user which Menu item was selected.

function Example() {
  let onAction = (value) => alert('Menu item selected: ' + value);

  return (
    <div>
      <MenuTrigger>
        <ActionButton>
            Menu Button
        </ActionButton>
        <Menu items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]} itemKey="name" onAction={onAction}>
          {item => <Item>{item.name}</Item>}
        </Menu>
      </MenuTrigger>
    </div>
  );
}

Props#


NameTypeDefaultDescription
childrenReactElement[]

The contents of the MenuTrigger, a trigger and a Menu. See the MenuTrigger Content section for more information on what to provide as children.

alignAlignmentWhere the Menu aligns with its trigger.
direction'bottom''top'Where the Menu opens relative to its trigger.
closeOnSelectbooleanWhether the Menu closes when a selection is made.
isOpenbooleanWhether the Menu loads open (controlled).
defaultOpenbooleanWhether the Menu loads open (uncontrolled).
shouldFlipboolean

Whether the element should flip its orientation when there is insufficient space for it to render within the view.

Events
NameTypeDefaultDescription
onOpenChange(isOpen: boolean) => voidHandler that is called when the Menu opens or closes.
NameTypeDefaultDescription

Visual Options#


Align and Direction#

View guidelines

<MenuTrigger align="start" direction="bottom" shouldFlip={false}>
  <ActionButton>
      align=start direction=bottom
  </ActionButton>
  <Menu items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]} itemKey="name">
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>
<MenuTrigger align="start" direction="top" shouldFlip={false}>
  <ActionButton>
      align=start direction=top
  </ActionButton>
  <Menu items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]} itemKey="name">
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>
<MenuTrigger align="end" direction="bottom" shouldFlip={false}>
  <ActionButton>
      align=end direction=bottom
  </ActionButton>
  <Menu items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]} itemKey="name">
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>
<MenuTrigger align="end" direction="top" shouldFlip={false}>
  <ActionButton>
      align=end direction=top
  </ActionButton>
  <Menu items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]} itemKey="name">
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>

Autofocus#

Applying autoFocus to the Menu of the MenuTrigger sets focus within the Menu upon opening.

<MenuTrigger>
  <ActionButton>
      autofocus
  </ActionButton>
  <Menu
    items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]}
    itemKey="name"
    selectionMode="single"
    selectedKeys={['Kangaroo']}
    autoFocus>
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>
<MenuTrigger>
  <ActionButton>
      autofocus=false
  </ActionButton>
  <Menu
    items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]}
    itemKey="name"
    selectionMode="single"
    selectedKeys={['Kangaroo']}
    autoFocus={false}>
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>
<MenuTrigger>
  <ActionButton>
      autofocus=first
  </ActionButton>
  <Menu
    items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]}
    itemKey="name"
    autoFocus="first">
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>
<MenuTrigger>
  <ActionButton>
      autofocus=last
  </ActionButton>
  <Menu
    items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]}
    itemKey="name"
    autoFocus="last">
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>

Closes Menu onSelect#

<MenuTrigger closeOnSelect>
  <ActionButton>
      closeOnSelect=true
  </ActionButton>
  <Menu
    items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]}
    itemKey="name"
    selectionMode="single">
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>
<MenuTrigger closeOnSelect={false}>
  <ActionButton>
      closeOnSelect=false
  </ActionButton>
  <Menu
    items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]}
    itemKey="name"
    selectionMode="single">
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>

Disabled Trigger#

<MenuTrigger>
  <ActionButton isDisabled>
      Disabled Menu Button
  </ActionButton>
  <Menu items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]} itemKey="name">
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>

Disabled Menu Items#

<MenuTrigger>
  <ActionButton>
      Disabled Menu Items
  </ActionButton>
  <Menu
    items={[{name: 'Aardvark', dataId: 'a1b2c3'}, {name: 'Kangaroo', dataId: 'g5h1j9'}, {name: 'Snake', dataId: 'p8k3i4'}]}
    itemKey="dataId"
    disabledKeys={['a1b2c3', 'p8k3i4']}>
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>

Flipping#

Applying shouldFlip to the MenuTrigger makes the Menu attempt to flip on its main axis in situations where the original placement would cause it to render out of view.

<MenuTrigger shouldFlip>
  <ActionButton>
      shouldFlip=true
  </ActionButton>
  <Menu items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]} itemKey="name">
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>
<MenuTrigger shouldFlip={false}>
  <ActionButton>
      shouldFlip=false
  </ActionButton>
  <Menu items={[{name: 'Aardvark'}, {name: 'Kangaroo'}, {name: 'Snake'}]} itemKey="name">
    {item => <Item>{item.name}</Item>}
  </Menu>
</MenuTrigger>

Open#

The isOpen and defaultOpen props control whether the MenuTrigger is open by default. They apply controlled and uncontrolled behavior on the MenuTrigger respectively.