An ActionMenu comprises a set of ActionList items, where each item represents an action, command, submenu, or the current selection that can be either a single or multi-select. The ActionMenu can be invoked by clicking on a Button , IconButton , or right-clicking on the list items.
code editor < ActionMenu >
< ActionMenu.Button > Open menu </ ActionMenu.Button >
< ActionMenu.Overlay >
< ActionList >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Item one clicked' )
} }
>
Item one
</ ActionList.Item >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Item two clicked' )
} }
>
Item two
</ ActionList.Item >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Item three clicked' )
} }
>
Item three
</ ActionList.Item >
</ ActionList >
</ ActionMenu.Overlay >
</ ActionMenu >
code editor < ActionMenu >
< ActionMenu.Button > Open menu </ ActionMenu.Button >
< ActionMenu.Overlay >
< ActionList >
< ActionList.Item loading > Item one </ ActionList.Item >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Item two clicked' )
} }
>
Item two
</ ActionList.Item >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Item three clicked' )
} }
>
Item three
</ ActionList.Item >
</ ActionList >
</ ActionMenu.Overlay >
</ ActionMenu >
Menu items affected by a system error, such as an outage, may become inactive and display a message explaining why.
If an ActionMenu (e.g., adding a file to a repo) has no available items, the trigger Button should be inactive .
Inactive ActionMenu items follow the same patterns as inactive list items, with two exceptions:
The unavailability message is displayed directly in the item, not in a tooltip, since the menu is hidden until opened.
No alert icon is needed, as the error message provides sufficient context.
Open menu Open menu (all items inactive) code editor < Stack align = " start " >
< ActionMenu >
< ActionMenu.Button > Open menu </ ActionMenu.Button >
< ActionMenu.Overlay >
< ActionList >
< ActionList.Item inactiveText = " Unavailable due to an outage " > Item one </ ActionList.Item >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Item two clicked' )
} }
>
Item two
</ ActionList.Item >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Item three clicked' )
} }
>
Item three
</ ActionList.Item >
</ ActionList >
</ ActionMenu.Overlay >
</ ActionMenu >
< ActionMenu >
< ActionMenu.Button inactive > Open menu (all items inactive) </ ActionMenu.Button >
< ActionMenu.Overlay >
< ActionList >
< ActionList.Item inactiveText = " Unavailable due to an outage " > Item one </ ActionList.Item >
< ActionList.Item inactiveText = " Unavailable due to an outage " > Item two </ ActionList.Item >
< ActionList.Item inactiveText = " Unavailable due to an outage " > Item three </ ActionList.Item >
</ ActionList >
</ ActionMenu.Overlay >
</ ActionMenu >
</ Stack >
For single-select menus that display the selection in a Button, a label must be persist either internally or externally.
code editor import React from 'react'
import { ActionList , ActionMenu } from '@primer/react'
const items = [ { label : 'Item one' } , { label : 'Item two' } , { label : 'Item three' } ]
export default function SingleSelect ( ) {
const [ selectedIndex , setSelectedIndex ] = React . useState ( 0 )
return (
< ActionMenu >
< ActionMenu.Button > Open menu </ ActionMenu.Button >
< ActionMenu.Overlay >
< ActionList selectionVariant = " single " role = " menu " aria-label = " Item " >
{ items . map ( ( item , index ) => (
< ActionList.Item
key = { index }
role = " menuitemradio "
selected = { index === selectedIndex }
aria-checked = { index === selectedIndex }
onSelect = { ( ) => setSelectedIndex ( index ) }
>
{ item . label }
</ ActionList.Item >
) ) }
</ ActionList >
</ ActionMenu.Overlay >
</ ActionMenu >
)
}
code editor import React from 'react'
import { ActionList , ActionMenu } from '@primer/react'
const items = [ { label : 'Item one' } , { label : 'Item two' } , { label : 'Item three' } ]
export default function MultiSelect ( ) {
const [ selectedIndices , setSelectedIndices ] = React . useState < number [ ] > ( [ 0 ] )
const handleSelect = ( index : number ) => {
if ( selectedIndices . includes ( index ) ) {
setSelectedIndices ( selectedIndices . filter ( i => i !== index ) )
} else {
setSelectedIndices ( [ ... selectedIndices , index ] )
}
}
return (
< ActionMenu >
< ActionMenu.Button > Open menu </ ActionMenu.Button >
< ActionMenu.Overlay >
< ActionList selectionVariant = " multiple " role = " menu " aria-label = " Item " >
{ items . map ( ( item , index ) => (
< ActionList.Item
key = { index }
role = " menuitemcheckbox "
selected = { selectedIndices . includes ( index ) }
aria-checked = { selectedIndices . includes ( index ) }
onSelect = { ( ) => handleSelect ( index ) }
>
{ item . label }
</ ActionList.Item >
) ) }
</ ActionList >
</ ActionMenu.Overlay >
</ ActionMenu >
)
}
To enhance the readability of menus that contain numerous item descriptions, it is recommended to incorporate dividers. This can effectively prevent the menu from becoming overwhelming.
code editor import React from 'react'
import { ActionList , ActionMenu } from '@primer/react'
export default function Dividers ( ) {
return (
< ActionMenu >
< ActionMenu.Button > Options </ ActionMenu.Button >
< ActionMenu.Overlay >
< ActionList showDividers aria-label = " Watch preference options " >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Item one clicked' )
} }
>
Item one
< ActionList.Description variant = " block " >
Description about item one that is kind of long and bulky
</ ActionList.Description >
</ ActionList.Item >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Item two clicked' )
} }
>
Item two
< ActionList.Description variant = " block " >
A long and bulky description for the second item
</ ActionList.Description >
</ ActionList.Item >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Item three clicked' )
} }
>
Item three
< ActionList.Description variant = " block " >
One last and long bulky description we will use for the third item
</ ActionList.Description >
</ ActionList.Item >
</ ActionList >
</ ActionMenu.Overlay >
</ ActionMenu >
)
}
A menu with multiple levels is a common UI component used in desktop and mobile applications. It allows users to access nested subitems without cluttering the screen.
Multi-level menu guidelines
code editor < ActionMenu >
< ActionMenu.Button > Open menu </ ActionMenu.Button >
< ActionMenu.Overlay >
< ActionList >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Item one clicked' )
} }
>
Item one
</ ActionList.Item >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Item two clicked' )
} }
>
Item two
</ ActionList.Item >
< ActionMenu >
< ActionMenu.Anchor >
< ActionList.Item > Item with submenus </ ActionList.Item >
</ ActionMenu.Anchor >
< ActionMenu.Overlay >
< ActionList >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Sub-item one clicked' )
} }
>
Sub-item one
</ ActionList.Item >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Sub-item two clicked' )
} }
>
Sub-item two
</ ActionList.Item >
< ActionMenu >
< ActionMenu.Anchor >
< ActionList.Item > Sub-item with submenu </ ActionList.Item >
</ ActionMenu.Anchor >
< ActionMenu.Overlay >
< ActionList >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Sub-item one clicked' )
} }
>
Sub-item one
</ ActionList.Item >
< ActionList.Item
onSelect = { ( ) => {
alert ( 'Sub-item two clicked' )
} }
>
Sub-item two
</ ActionList.Item >
</ ActionList >
</ ActionMenu.Overlay >
</ ActionMenu >
</ ActionList >
</ ActionMenu.Overlay >
</ ActionMenu >
</ ActionList >
</ ActionMenu.Overlay >
</ ActionMenu >
Menus can be triggered through a Button, IconButton or right clicking list items.
Right click the list items below to see the context menu
List item one List item two List item three code editor import React from 'react'
import { ActionList , ActionMenu , Button } from '@primer/react'
function ListItemWithContextMenu ( { children } : { children : string } ) {
const handleContextMenu : React . MouseEventHandler < HTMLElement > = event => {
event . preventDefault ( )
setOpen ( true )
}
const [open, setOpen] = React.useState(false)
const triggerRef = React.useRef < HTMLButtonElement > (null)
return (
< li onContextMenu = { handleContextMenu } >
< ActionMenu open = { open } onOpenChange = { setOpen } anchorRef = { triggerRef } >
< ActionMenu.Anchor >
< Button ref = { triggerRef } variant = " invisible " onClick = { handleContextMenu } >
{ children }
</ Button >
</ ActionMenu.Anchor >
< ActionMenu.Overlay >
< ActionList >
< ActionList.Item > Menu item one </ ActionList.Item >
< ActionList.Item > Menu item two </ ActionList.Item >
< ActionList.Item > Menu item three </ ActionList.Item >
</ ActionList >
</ ActionMenu.Overlay >
</ ActionMenu >
</ li >
)
}
export default function ContextMenu() {
return (
< >
< div > Right click the list items below to see the context menu </ div >
< ul >
< ListItemWithContextMenu > List item one </ ListItemWithContextMenu >
< ListItemWithContextMenu > List item two </ ListItemWithContextMenu >
< ListItemWithContextMenu > List item three </ ListItemWithContextMenu >
</ ul >
</ >
)
}
Since ActionMenu
is a wrapper around ActionList
, the menu items support almost all of the same features as ActionList items.
See the ActionMenu Storybook stories .
children
Required React.ReactElement[]
Recommended: `ActionMenu.Button` or `ActionMenu.Anchor` with `ActionMenu.Overlay`
open
boolean
If defined, will control the open/closed state of the overlay. Must be used in conjuction with `onOpenChange`.
onOpenChange
(open: boolean) => void
If defined, will control the open/closed state of the overlay. Must be used in conjuction with `open`.
anchorRef
React.RefObject<HTMLElement>
Useful for defining an external anchor
children
Required React.ReactElement
Accepts a single child element
id
children
Required align
side
| 'inside-top' | 'inside-bottom' | 'inside-left' | 'inside-right' | 'inside-center' | 'outside-top' | 'outside-bottom' | 'outside-left' | 'outside-right'
Controls which side of the anchor the menu will appear
data-test-id
unknown
ID to use for React testing utilities.