import {Layout} from '../../src/Layout'; export default Layout;
import docs from 'docs:react-aria-components'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/MenuAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2'
export const tags = ['dropdown'];
{docs.exports.Menu.description}
```tsx render type="vanilla" files={["starters/docs/src/Menu.tsx", "starters/docs/src/Menu.css"]} "use client"; import {MenuTrigger, Menu, Popover, SubmenuTrigger, MenuSection, Separator} from 'react-aria-components'; import {Button} from 'vanilla-starter/Button'; import {MenuItem} from 'vanilla-starter/Menu'; ☰ alert('open')}>Open alert('rename')}>Rename… alert('duplicate')}>Duplicate alert('share')}>Share… Share Email SMS Instagram alert('delete')}>Delete… Show files Show folders ```"use client";
import {MenuTrigger, SubmenuTrigger} from 'react-aria-components';
import {Menu, MenuItem, MenuSection, MenuSeparator} from 'tailwind-starter/Menu';
import {Button} from 'tailwind-starter/Button';
import {MoreHorizontal} from 'lucide-react';
<MenuTrigger>
<Button aria-label="Actions" variant="icon">
<MoreHorizontal className="w-5 h-5" />
</Button>
<Menu>
<MenuItem onAction={() => alert('open')}>Open</MenuItem>
<MenuItem onAction={() => alert('rename')}>Rename…</MenuItem>
<MenuItem onAction={() => alert('duplicate')}>Duplicate</MenuItem>
<MenuItem onAction={() => alert('share')}>Share…</MenuItem>
<SubmenuTrigger>
<MenuItem>Share</MenuItem>
<Menu>
<MenuItem>Email</MenuItem>
<MenuItem>SMS</MenuItem>
<MenuItem>Instagram</MenuItem>
</Menu>
</SubmenuTrigger>
<MenuItem onAction={() => alert('delete')}>Delete…</MenuItem>
<MenuSeparator />
<MenuSection selectionMode="multiple" defaultSelectedKeys={['files']}>
<MenuItem id="files">Show files</MenuItem>
<MenuItem id="folders">Show folders</MenuItem>
</MenuSection>
</Menu>
</MenuTrigger>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.
"use client";
import {MenuTrigger, Menu, MenuItem, Button, Popover} from 'react-aria-components';
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>
<Popover>
{/*- begin highlight -*/}
<Menu items={items}>
{(item) => <MenuItem>{item.name}</MenuItem>}
</Menu>
{/*- end highlight -*/}
</Popover>
</MenuTrigger>
);
}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.
"use client";
import {MenuTrigger, Menu, MenuItem, Text, Keyboard, Button, Popover} from 'react-aria-components';
<MenuTrigger>
<Button>Permissions</Button>
<Popover>
<Menu>
<MenuItem textValue="Copy">
{/*- begin highlight -*/}
<Text slot="label">Copy</Text>
<Text slot="description">Copy the selected text</Text>
{/*- end highlight -*/}
<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>
</Popover>
</MenuTrigger>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.
"use client";
import {MenuTrigger, Menu, MenuItem, MenuSection, Header, Button, Popover} from 'react-aria-components';
<MenuTrigger>
<Button>Publish</Button>
<Popover>
<Menu>
{/*- begin highlight -*/}
<MenuSection>
<Header>Export</Header>
{/*- end highlight -*/}
<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>
</Popover>
</MenuTrigger>Wrap a <MenuItem> and a <Popover> with a <SubmenuTrigger> to create a submenu.
"use client";
import {MenuTrigger, Menu, Popover, SubmenuTrigger, Button, Popover} from 'react-aria-components';
import {MenuItem} from 'vanilla-starter/Menu';
<MenuTrigger>
<Button>Actions</Button>
<Popover>
<Menu>
<MenuItem>Cut</MenuItem>
<MenuItem>Copy</MenuItem>
<MenuItem>Delete</MenuItem>
{/*- begin highlight -*/}
<SubmenuTrigger>
<MenuItem>Share</MenuItem>
{/*- end highlight -*/}
<Menu>
<MenuItem>SMS</MenuItem>
<MenuItem>Instagram</MenuItem>
<SubmenuTrigger>
<MenuItem>Email</MenuItem>
<Popover>
<Menu>
<MenuItem>Work</MenuItem>
<MenuItem>Personal</MenuItem>
</Menu>
</Popover>
</SubmenuTrigger>
</Menu>
</SubmenuTrigger>
</Menu>
</Popover>
</MenuTrigger>Separators may be added between menu items or sections in order to create non-labeled groupings.
"use client";
import {MenuTrigger, Menu, MenuItem, Separator, Button, Popover} from 'react-aria-components';
<MenuTrigger>
<Button>Actions</Button>
<Popover>
<Menu>
<MenuItem>New…</MenuItem>
<MenuItem>Open…</MenuItem>
{/*- begin highlight -*/}
<Separator />
{/*- end highlight -*/}
<MenuItem>Save</MenuItem>
<MenuItem>Save as…</MenuItem>
<MenuItem>Rename…</MenuItem>
<Separator />
<MenuItem>Page setup…</MenuItem>
<MenuItem>Print…</MenuItem>
</Menu>
</Popover>
</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.
"use client";
import {MenuTrigger, Menu, MenuItem, Button, Popover} from 'react-aria-components';
<MenuTrigger>
<Button>Links</Button>
<Popover>
<Menu>
{/*- begin highlight -*/}
<MenuItem href="https://adobe.com/" target="_blank">Adobe</MenuItem>
{/*- end highlight -*/}
<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>
</Popover>
</MenuTrigger>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.
"use client";
import {Autocomplete, useFilter} from 'react-aria-components';
import {MenuTrigger, Menu, MenuItem} from 'vanilla-starter/Menu';
import {Button} from 'vanilla-starter/Button';
import {Popover} from 'vanilla-starter/Popover';
import {SearchField} from 'vanilla-starter/SearchField';
function Example() {
let {contains} = useFilter({sensitivity: 'base'});
return (
<MenuTrigger>
<Button>Add tag...</Button>
<Popover>
{/*- begin highlight -*/}
<Autocomplete filter={contains}>
<SearchField label="Search tags" autoFocus />
<Menu>
{/*- end highlight -*/}
<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>
);
}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.
"use client";
import type {Selection} from 'react-aria-components';
import {MenuTrigger, Menu, MenuItem, Button, Popover} from 'react-aria-components';
import {useState} from 'react';
function Example(props) {
let [selected, setSelected] = useState<Selection>(new Set(['rulers']));
return (
<>
<MenuTrigger>
<Button>View</Button>
<Popover>
<Menu
{...props}
///- begin highlight -///
/* PROPS */
selectedKeys={selected}
onSelectionChange={setSelected}
///- end highlight -///
>
<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>
</Popover>
</MenuTrigger>
<p>Current selection: {selected === 'all' ? 'all' : [...selected].join(', ')}</p>
</>
);
}Each section in a menu may have independent selection states by passing selectionMode and selectedKeys to the MenuSection.
"use client";
import type {Selection} from 'react-aria-components';
import {MenuTrigger, Menu, MenuItem, MenuSection, Header, Button, Popover} from 'react-aria-components';
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>
<Popover>
<Menu>
<MenuSection>
<Header>Clipboard</Header>
<MenuItem>Cut</MenuItem>
<MenuItem>Copy</MenuItem>
<MenuItem>Paste</MenuItem>
</MenuSection>
{/*- begin highlight -*/}
<MenuSection
selectionMode="multiple"
selectedKeys={style}
onSelectionChange={setStyle}>
{/*- end highlight -*/}
<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>
</Popover>
</MenuTrigger>
);
}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.
"use client";
import {MenuTrigger, Menu, MenuItem, Button, Popover, Pressable} from 'react-aria-components';
<MenuTrigger>
{/*- begin highlight -*/}
<Pressable>
<span role="button">Custom trigger</span>
</Pressable>
{/*- end highlight -*/}
<Popover>
<Menu>
<MenuItem>Open</MenuItem>
<MenuItem>Rename…</MenuItem>
<MenuItem>Duplicate</MenuItem>
<MenuItem>Delete…</MenuItem>
</Menu>
</Popover>
</MenuTrigger>const CustomTrigger = React.forwardRef((props, ref) => (
<button {...props} ref={ref} />
));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.
"use client";
import {MenuTrigger, Menu, MenuItem, Button, Popover} from 'react-aria-components';
<MenuTrigger trigger="longPress">
<Button onPress={() => alert('crop')}>Crop ▼</Button>
<Popover>
<Menu>
<MenuItem>Rotate</MenuItem>
<MenuItem>Slice</MenuItem>
<MenuItem>Clone stamp</MenuItem>
</Menu>
</Popover>
</MenuTrigger><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>