Menu
An accessible dropdown menu for the common dropdown menu button design pattern. Menu uses roving tabIndex for focus management.
Import#
import { Menu } from '@chakra-ui/react'
Usage#
<Menu.Root><Menu.Trigger asChild><Button>Actions<FiChevronDown /></Button></Menu.Trigger><Menu.Positioner><Menu.Content><Menu.Item>Download</Menu.Item><Menu.Item>Create a Copy</Menu.Item><Menu.Item>Mark as Draft</Menu.Item><Menu.Item>Delete</Menu.Item><Menu.Item>Attend a Workshop</Menu.Item></Menu.Conte></Menu.Positioner></Menu.Root>
Accessing the internal state#
To access the internal state of the Menu.Root
, use a function as children
(commonly known as a render prop). You'll get access to the internal state
open
and method onClose
.
<Menu.Root>{(state) => (<><Menu.Trigger asChild><Button active={state.open}>{state.open ? 'Close' : 'Open'} <FiChevronDown /></Button></Menu.Trigger><Menu.Positioner><Menu.Content><Menu.Item>Download</Menu.Item><Menu.Item onClick={() => alert('Kagebunshin')}>Create a Copy</Menu.Item></Menu.Content></Menu.Positioner></>)}</Menu>
Rendering icon and command#
<Menu.Root><Menu.Trigger asChild aria-label='Options'><IconButton variant='outline'><FiMenu /></IconButton></Menu.Trigger><Menu.Positioner><Menu.Content><Menu.Item><Menu.Icon><FiPlus /></Menu.Icon>New Tab<Menu.Command><kbd>⌘T</kbd></Menu.Command></Menu.Item><Menu.Item><Menu.Icon><FiExternalLink /></Menu.Icon>New Window<Menu.Command><kbd>⌘N</kbd></Menu.Command></Menu.Item><Menu.Item><Menu.Icon><BiShare /></Menu.Icon>Open File...<Menu.Command><kbd>⌘O</kbd></Menu.Command></Menu.Item></Menu.Content></Menu.Positioner></Menu.Root>
Lazily mounting Menu.Item#
By default, the Menu.Root
component renders all children of Menu.Content
to
the DOM, meaning that invisible menu items are still rendered but are hidden by
styles.
If you want to defer rendering of each children of Menu.Content
until that
menu is open, you can use the isLazy
prop.
This is useful to improve performance.
<Menu.Root lazyMount><Menu.Trigger>Open menu</Menu.Trigger><Menu.Positioner><Menu.Content><Menu.Item>New Window</Menu.Item><Menu.Item>Open Closed Tab</Menu.Item><Menu.Item>Open File</Menu.Item></Menu.Content></Menu.Positioner></Menu.Root>
Rendering menu in a portal#
To render menus in a portal, import the Portal
component and wrap the
Menu.Content
within the Portal
.
<Menu.Root><Menu.Trigger>Open menu</Menu.Trigger><Portal><Menu.Positioner><Menu.Content><Menu.Item>Menu 1</Menu.Item><Menu.Item>New Window</Menu.Item><Menu.Item>Open Closed Tab</Menu.Item><Menu.Item>Open File</Menu.Item></Menu.Content></Menu.Positioner></Portal></Menu.Root>
Grouping menu items#
To group related menu items, use the MenuGroup
component and pass it a title
for the group name.
<Menu.Root><Menu.Trigger asChild><Button>Profile</Button></Menu.Trigger><Menu.Positioner><Menu.Content><Menu.Group title='Profile'><Menu.Item>My Account</Menu.Item><Menu.Item>Payments </Menu.Item></Menu.Group><Menu.Separator /><MenuGroup title='Help'><Menu.Item>Docs</Menu.Item><Menu.Item>FAQ</Menu.Item></MenuGroup></Menu.Content></Menu.Positioner></Menu.Root>
Rendering menu items as links#
To render a Menu.Item
as a link, use the attributes asChild
prop
<Menu.Root><Menu.Trigger>Open menu</Menu.Trigger><Menu.Positioner><Menu.Content><Menu.Item asChild><a href='#'>Link 1</a></Menu.Item><Menu.Item asChild><a href='#'>Link 2</a></Menu.Item></Menu.Content></Menu.Positioner></Menu.Root>
Menu radio and checkbox#
You can compose a menu for table headers to help with sorting and filtering
options. Use the Menu.OptionGroup
and Menu.OptionItem
components.
<Menu.Root closeOnSelect={false}><Menu.Trigger asChild><Button>Open Menu</Button></Menu.Trigger><Menu.Positioner><Menu.Content minWidth='240px'><Menu.OptionGroup defaultValue='asc' title='Order' type='radio'><Menu.OptionItem value='asc'>Ascending</Menu.OptionItem><Menu.OptionItem value='desc'>Descending</Menu.OptionItem></Menu.OptionGroup><Menu.Separator /><Menu.OptionGroup title='Country' type='checkbox'><Menu.OptionItem value='email'>Email</Menu.OptionItem><Menu.OptionItem value='phone'>Phone</Menu.OptionItem><Menu.OptionItem value='country'>Country</Menu.OptionItem></Menu.OptionGroup></Menu.Content></Menu.Positioner></Menu.Root>
Accessibility#
Keyboard Interaction#
Key | Action |
---|---|
Enter or Space | When Menu.Trigger receives focus, opens the menu and places focus on the first menu item. |
ArrowDown | When Menu.Trigger receives focus, opens the menu and moves focus to the first menu item. |
ArrowUp | When Menu.Trigger receives focus, opens the menu and moves focus to the last menu item. |
Escape | When the menu is open, closes the menu and sets focus to the Menu.Trigger . |
Tab | no effect |
Home | When the menu is open, moves focus to the first item. |
End | When the menu is open, moves focus to the last item. |
A-Z or a-z | When the menu is open, moves focus to the next menu item with a label that starts with the typed character if such an menu item exists. |
ARIA roles#
For Menu.Trigger
:
role
is set tobutton
.aria-haspopup
is set tomenu.Root
.- When the menu is displayed,
aria-expanded
is set totrue
. aria-controls
is set to theid
of theMenu.Content
.
For Menu.Content
:
role
is set tomenu.Root
.aria-orientation
is set tovertical
.
For Menu.Item
:
role
is set tomenu.item
.- Gets one of these roles
menu.item
/menu.itemradio
/menu.itemcheckbox
.
Props#
Menu Props#
MenuButton Props#
MenuButton
composes Box so you can pass all
Box
props to change its style.
MenuList Props#
MenuList
composes Box so you can pass all Box
props to change its style.
MenuItem Props#
MenuGroup Props#
MenuGroup
composes Box so you can pass all
Box
props to change its style.
MenuOptionGroup Props#
MenuItemOption Props#
MenuItemOption
composes Box so you can pass all
box props in addition to these:
The Menu
component is a multipart component. The styling needs to be applied
to each part specifically.
To learn more about styling multipart components, visit the Component Style page.
Anatomy#
- A:
button
- B:
list
- C:
item
- D:
groupTitle
- E:
command
- F:
divider
Theming properties#
The properties that affect the theming of the Menu
component are:
variant
: The visual variant of the component. There is no default applied.size
: The size of the component. There is no default applied.
Theming utilities#
createMultiStyleConfigHelpers
: a function that returns a set of utilities for creating style configs for a multipart component (definePartsStyle
anddefineMultiStyleConfig
).definePartsStyle
: a function used to create multipart style objects.defineMultiStyleConfig
: a function used to define the style configuration for a multipart component.
import { menuAnatomy } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers, defineStyle } from '@chakra-ui/react'const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(menuAnatomy.keys)
Customizing the default theme#
import { menuAnatomy } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers, defineStyle } from '@chakra-ui/react'const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(menuAnatomy.keys)// define the base component stylesconst baseStyle = definePartsStyle({// define the part you're going to stylebutton: {// this will style the MenuButton componentfontWeight: 'medium',bg: 'teal.500',color: 'gray.200',_hover: {bg: 'teal.600',color: 'white',},},list: {// this will style the MenuList componentpy: '4',borderRadius: 'xl',border: 'none',bg: 'teal.500',},item: {// this will style the MenuItem and MenuItemOption componentscolor: 'gray.200',_hover: {bg: 'teal.600',},_focus: {bg: 'teal.600',},},groupTitle: {// this will style the text defined by the title prop// in the MenuGroup and MenuOptionGroup componentstextTransform: 'uppercase',color: 'white',textAlign: 'center',letterSpacing: 'wider',opacity: '0.7',},command: {// this will style the text defined by the command// prop in the MenuItem and MenuItemOption componentsopacity: '0.8',fontFamily: 'mono',fontSize: 'sm',letterSpacing: 'tighter',pl: '4',},divider: {// this will style the MenuDivider componentmy: '4',borderColor: 'white',borderBottom: '2px dotted',},})// export the base styles in the component themeexport const menuTheme = defineMultiStyleConfig({ baseStyle })
After customizing the input theme, we can import it into our theme file and add
it in the components
property:
import { extendTheme } from '@chakra-ui/react'import { menuTheme } from './components/Menu'const theme = extendTheme({components: {Menu: menuTheme,},})export default theme
This is a crucial step to make sure that any changes we make to the menu theme are applied.
Adding a custom size#
Let's assume we want to include an extra large menu size. Here's how we can do that:
import { menuAnatomy } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers, defineStyle } from '@chakra-ui/react'const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(menuAnatomy.keys)// define custom stylesconst lg = defineStyle({fontSize: 'md',my: '1',})const xl = defineStyle({fontSize: 'lg',px: '4',py: '2',})// define custom sizesconst sizes = {// apply custom styles to partsxl: definePartsStyle({ button: xl, item: xl, groupTitle: lg, command: xl }),}// export the sizes in the component themeexport const menuTheme = defineMultiStyleConfig({ sizes })// now we can use the new `xl` size<Menu size="xl" ... />
Every time you add anything new to the theme, you need to run the CLI command to get proper autocomplete in your IDE. You can learn more about the CLI tool here.
Adding a custom variant#
Let's assume we want to include some custom variants to create a pill-shaped menu bar. Here's how we can do that:
import { menuAnatomy } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers, defineStyle } from '@chakra-ui/react'const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(menuAnatomy.keys)// define custom variantsconst variants = {roundLeft: {button: {borderLeftRadius: 'full',pl: '6',},},roundRight: {button: {borderRightRadius: 'full',pr: '6',},},}// export the custom variants in the component themeexport const menuTheme = defineMultiStyleConfig({ variants })// now we can use the new `roundLeft` and `roundRight` variants<Menu variant="roundLeft" ... /><Menu variant="roundRight" ... />
Changing the default properties#
Let's assume we want to change the default size and variant of every menu in our app. Here's how we can do that:
import { menuAnatomy } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers, defineStyle } from '@chakra-ui/react'const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(menuAnatomy.keys)// define which sizes and variants are applied by defaultconst defaultProps = {// in this example, we will only set a default sizesize: 'xl',}// export the default properties in the component themeexport const menuTheme = defineMultiStyleConfig({ defaultProps })
Showcase#
import { ChakraProvider, Menu, MenuButton, MenuList, MenuItem, MenuItemOption, MenuGroup, MenuOptionGroup, MenuDivider, Box, Center, } from '@chakra-ui/react'; import theme from './theme'; import { ColorModeSwitcher } from './ColorModeSwitcher'; export default function App() { return ( <ChakraProvider theme={theme}> <Box position="relative" h="100vh" p={12}> <Center> <Menu variant="roundLeft"> <MenuButton>File</MenuButton> <MenuList> <MenuItem command="Ctrl + N">New File</MenuItem> <MenuItem command="Ctrl + O">Open File</MenuItem> <MenuDivider /> <MenuGroup title="Save"> <MenuItem command="Ctrl + S">Save</MenuItem> <MenuItem command="Ctrl + Shift + S">Save As...</MenuItem> <MenuItem command="Ctrl + Alt + S">Save All</MenuItem> </MenuGroup> <MenuDivider /> <MenuItem>Exit</MenuItem> </MenuList> </Menu> <Menu> <MenuButton>Edit</MenuButton> <MenuList> <MenuItem command="Ctrl + Z">Undo</MenuItem> <MenuItem command="Ctrl + Y">Redo</MenuItem> <MenuDivider /> <MenuGroup> <MenuItem command="Ctrl + X">Cut</MenuItem> <MenuItem command="Ctrl + C">Copy</MenuItem> <MenuItem command="Ctrl + V">Paste</MenuItem> </MenuGroup> </MenuList> </Menu> <Menu variant="roundRight"> <MenuButton>View</MenuButton> <MenuList> <MenuItem command="Ctrl + F">Full Screen Mode</MenuItem> <MenuItem command="Ctrl + R">Reading Mode</MenuItem> <MenuDivider /> <MenuGroup title="Zoom"> <MenuItem command="Ctrl + 1">Actual Size</MenuItem> <MenuItem command="Ctrl + 2">Fit Width</MenuItem> <MenuItem command="Ctrl + 3">Height</MenuItem> </MenuGroup> <MenuDivider /> <MenuOptionGroup title="Display Size" type="radio" defaultValue={'standard'} > <MenuItemOption value="small" closeOnSelect={false}> Small </MenuItemOption> <MenuItemOption value="standard" closeOnSelect={false}> Standard </MenuItemOption> <MenuItemOption value="large" closeOnSelect={false}> Large </MenuItemOption> </MenuOptionGroup> </MenuList> </Menu> </Center> <ColorModeSwitcher aria-label="toggle theme" position="absolute" bottom={4} left={4} /> </Box> </ChakraProvider> ); }