Dropdown menu
A dropdown menu displays a list of actions when a trigger is pressed.
import { ... } from '@ag.ds-next/react/dropdown-menu';
<DropdownMenu> <DropdownMenuButton>Toggle dropdown menu</DropdownMenuButton> <DropdownMenuPanel> <DropdownMenuItem onClick={() => console.log('Profile')}> Profile </DropdownMenuItem> <DropdownMenuItem onClick={() => console.log('Messages')}> Messages </DropdownMenuItem> <DropdownMenuItem onClick={() => console.log('Account settings')}> Account settings </DropdownMenuItem> </DropdownMenuPanel> </DropdownMenu>
A dropdown menu displays a list of actions when triggered by a DropdownMenuButton
. Dropdown menus float on top of the content they sit on. Consider using them when there isn’t sufficient space to display actions.
Dropdown menus pose multiple usability problems which you should consider before using them:
- since the actions in a dropdown menu are hidden by default, they could be missed.
- dropdown menus can increase cognitive load, as users need to recall what the actions were. This can be especially problematic for users with cognitive impairments.
- due to their fiddly nature, some users can struggle to use dropdown menus. Elderly people and people with motor impairments, in particular, often have difficulty using dropdown menus.
Do
- ensure that actions in the dropdown can be accessed elsewhere on the same page, as some users won’t be able to access them via the dropdown menu
- only use a dropdown menu if there’s insufficient space to display actions
- trigger a dropdown menu on click of an element
- keep the dropdown menu open until a user has made a selection or clicked outside the menu
- organise actions in a logical way
- consider listing actions in alphabetical order by default
- group related actions
- consider setting a maximum height when there are 10 or more actions.
Don’t
- use a dropdown menu if there’s space to display actions
- trigger a dropdown menu on hover of an element, ensure it’s triggered on click
- use in the Main nav or other nav components
- use a dropdown menu in a form where users need to select a single option - use a Select component instead.
Triggers
Dropdown menus can be triggered by a DropdownMenuButton
, which can accept all of the same props as Button. This gives you flexibility in what your trigger looks like.
<Stack gap={1}> {['md', 'sm'].map((size) => ( <ButtonGroup key={size}> {['primary', 'secondary', 'tertiary', 'text'].map((variant) => ( <DropdownMenu key={variant}> <DropdownMenuButton variant={variant} size={size}> Actions </DropdownMenuButton> <DropdownMenuPanel> <DropdownMenuItem>Item A</DropdownMenuItem> <DropdownMenuItem>Item B</DropdownMenuItem> <DropdownMenuItem>Item C</DropdownMenuItem> </DropdownMenuPanel> </DropdownMenu> ))} </ButtonGroup> ))} </Stack>
Placement
By default the dropdown menu is aligned to the bottom left (bottom-start
) of the DropdownMenuButton
when opened. It can alternatively be aligned to the bottom right (bottom-end
) or the bottom (bottom
). If there’s not enough space in the browser window to align the menu on the left, the dropdown menu will automatically be aligned to the right to help ensure it fits on the screen.
<Stack gap={2} alignItems="center"> {['bottom-start', 'bottom-end', 'bottom'].map((size) => ( <DropdownMenu key={size} popoverPlacement={size}> <DropdownMenuButton>{size}</DropdownMenuButton> <DropdownMenuPanel> <DropdownMenuItem>Profile</DropdownMenuItem> <DropdownMenuItem>Messages</DropdownMenuItem> <DropdownMenuItem>Account settings</DropdownMenuItem> </DropdownMenuPanel> </DropdownMenu> ))} </Stack>
Text only
The simplest dropdown menu contains text only list items. Use DropdownMenuItem
to perform on-page actions and DropdownMenuItemLink
to link to separate pages.
<DropdownMenu> <DropdownMenuButton>Toggle dropdown menu</DropdownMenuButton> <DropdownMenuPanel> <DropdownMenuItem onClick={() => console.log('Profile')}> Profile </DropdownMenuItem> <DropdownMenuItem onClick={() => console.log('Messages')}> Messages </DropdownMenuItem> <DropdownMenuItemLink href="#"> Account settings </DropdownMenuItem> </DropdownMenuPanel> </DropdownMenu>
If you’re opening links in a new tab, we recommend you use the ExternalLinkIcon
.
<DropdownMenu> <DropdownMenuButton>Toggle dropdown menu</DropdownMenuButton> <DropdownMenuPanel> <DropdownMenuItemLink href="#link-1">Link 1</DropdownMenuItemLink> <DropdownMenuItemLink href="#link-2">Link 2</DropdownMenuItemLink> <DropdownMenuItemLink href="https://www.agriculture.gov.au" target="_blank" icon={ExternalLinkIcon} > External link </DropdownMenuItemLink> </DropdownMenuPanel> </DropdownMenu>
Icons and badges
Icons can be added to list items to help users recognise actions more quickly. Use the icon prop to add an Icon to the beginning of list items.
Use the endElement
prop to add elements such as an Indicator dot or Notification badge to the end of list items.
<DropdownMenu> <DropdownMenuButton>Toggle dropdown menu</DropdownMenuButton> <DropdownMenuPanel> <DropdownMenuItem icon={AvatarIcon}>Profile</DropdownMenuItem> <DropdownMenuItem icon={EmailIcon} endElement={ <React.Fragment> <NotificationBadge value={100} max={99} tone="action" aria-hidden /> <VisuallyHidden>, 99+ unread</VisuallyHidden> </React.Fragment> } > Messages </DropdownMenuItem> <DropdownMenuItem icon={SettingsIcon}>Account settings</DropdownMenuItem> </DropdownMenuPanel> </DropdownMenu>
Divider
Use a DropdownMenuDivider
to separate destructive actions at the bottom of the list. This helps ensure they’re less likely to be actioned by mistake.
<DropdownMenu> <DropdownMenuButton>Toggle dropdown menu</DropdownMenuButton> <DropdownMenuPanel> <DropdownMenuItem icon={AvatarIcon}>Profile</DropdownMenuItem> <DropdownMenuItem icon={EmailIcon} endElement={ <React.Fragment> <NotificationBadge value={100} max={99} tone="action" aria-hidden /> <VisuallyHidden>, 99+ unread</VisuallyHidden> </React.Fragment> } > Messages </DropdownMenuItem> <DropdownMenuItem icon={SettingsIcon}>Account settings</DropdownMenuItem> <DropdownMenuDivider /> <DropdownMenuItem icon={ExitIcon}>Sign out</DropdownMenuItem> </DropdownMenuPanel> </DropdownMenu>
Grouping
Consider grouping long lists of items under headings to help decrease cognitive load. Each group should be separated by a DropdownMenuDivider
.
Use the DropdownMenuGroup
component to group related items. Use the label prop to describe the group.
<DropdownMenu> <DropdownMenuButton>Toggle dropdown menu</DropdownMenuButton> <DropdownMenuPanel> <DropdownMenuGroup label="Services"> <DropdownMenuItem icon={PrintIcon}>Print online</DropdownMenuItem> <DropdownMenuItem icon={CalendarIcon}>Scheduler</DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuDivider /> <DropdownMenuGroup label="Account"> <DropdownMenuItem icon={AvatarIcon}>Profile</DropdownMenuItem> <DropdownMenuItem icon={EmailIcon} endElement={ <React.Fragment> <NotificationBadge value={100} max={99} tone="action" aria-hidden /> <VisuallyHidden>, 99+ unread</VisuallyHidden> </React.Fragment> } > Messages </DropdownMenuItem> <DropdownMenuItem icon={SettingsIcon}>Account settings</DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuDivider /> <DropdownMenuItem icon={ExitIcon}>Sign out</DropdownMenuItem> </DropdownMenuPanel> </DropdownMenu>
Radio groups
Use the DropdownMenuGroup
and DropdownMenuRadioItem
components to create a list of options that users can switch between. Only 1 item can be selected at a time, similar to a list of Radios.
Use the DropdownMenuGroupLink
component to add a link to the bottom of the Radio group.
() => { const [selectedItem, setSelectedItem] = React.useState(''); return ( <Stack gap={2} alignItems="flex-start"> <DropdownMenu> <DropdownMenuButton>Toggle dropdown menu</DropdownMenuButton> <DropdownMenuPanel> <DropdownMenuGroup label="Businesses"> <DropdownMenuItemRadio checked={selectedItem === 'Antfix'} onClick={() => setSelectedItem('Antfix')} secondaryText="Sydney" > Antfix </DropdownMenuItemRadio> <DropdownMenuItemRadio checked={selectedItem === 'Produce Fresh'} onClick={() => setSelectedItem('Produce Fresh')} secondaryText="Brisbane" > Produce Fresh </DropdownMenuItemRadio> <DropdownMenuItemRadio checked={selectedItem === 'Organic Co'} onClick={() => setSelectedItem('Organic Co')} secondaryText="Canberra" > Organic Co </DropdownMenuItemRadio> <DropdownMenuGroupLink href="#">View all</DropdownMenuGroupLink> </DropdownMenuGroup> </DropdownMenuPanel> </DropdownMenu> <Text> Currently selected: {selectedItem ? selectedItem : 'Unselected'} </Text> </Stack> ); };
Overflow
For dropdown menus with many items, consider setting a maximum height using the maxHeight
prop. This will make the actions in the dropdown menu scrollable. If the height of the actions list is taller than the screen height, the dropdown menu will automatically resize to ensure that it fits on the screen.
<Stack gap={2} alignItems="flex-start"> <DropdownMenu> <DropdownMenuButton> Toggle dropdown menu (without max height) </DropdownMenuButton> <DropdownMenuPanel> {Array.from(Array(20).keys()).map((i) => ( <DropdownMenuItem key={i}>Item {i + 1}</DropdownMenuItem> ))} </DropdownMenuPanel> </DropdownMenu> <DropdownMenu popoverMaxHeight={288}> <DropdownMenuButton> Toggle dropdown menu (with max height) </DropdownMenuButton> <DropdownMenuPanel> {Array.from(Array(20).keys()).map((i) => ( <DropdownMenuItem key={i}>Item {i + 1}</DropdownMenuItem> ))} </DropdownMenuPanel> </DropdownMenu> </Stack>
Offset
Use the popoverOffset
prop to control the vertical distance between the button and panel. This value needs to be specified in pixels
<DropdownMenu popoverOffset={-8}> <DropdownMenuButton variant="primary"> Toggle dropdown menu </DropdownMenuButton> <DropdownMenuPanel> <DropdownMenuItem onClick={() => console.log('Profile')}> Profile </DropdownMenuItem> <DropdownMenuItem onClick={() => console.log('Messages')}> Messages </DropdownMenuItem> <DropdownMenuItem onClick={() => console.log('Account settings')}> Account settings </DropdownMenuItem> </DropdownMenuPanel> </DropdownMenu>
Accessing state
If you need to access the internal state of the menu, you can provide a render callback to DropdownMenu
. This is useful if you want to change the appearance of the dropdown menu button based on whether the dropdown menu is opened or closed.
<DropdownMenu> {({ isMenuOpen }) => ( <React.Fragment> <DropdownMenuButton iconAfter={isMenuOpen ? ChevronUpIcon : ChevronDownIcon} > {isMenuOpen ? 'Close dropdown menu' : 'Open dropdown menu'} </DropdownMenuButton> <DropdownMenuPanel> <DropdownMenuItem>Profile</DropdownMenuItem> <DropdownMenuItem>Messages</DropdownMenuItem> <DropdownMenuItem>Account settings</DropdownMenuItem> </DropdownMenuPanel> </React.Fragment> )} </DropdownMenu>