The padding required to prevent the arrow from reaching the very edge of the popper.
ChatGPT UI Menu - Horizon AI Template
An accessible dropdown menu for the common dropdown menu button design pattern.
Menu
uses roving tabIndex
for focus management.
Import#
Chakra UI exports 8 components for rendering menus:
Menu
: The wrapper component provides context, state, and focus management.MenuList
: The wrapper for the menu items. Must be a direct child ofMenu
.MenuButton
: The trigger for the menu list. Must be a direct child ofMenu
.MenuItem
: The trigger that handles menu selection. Must be a direct child of aMenuList
.MenuGroup
: A wrapper to group related menu items.MenuDivider
: A visual separator for menu items and groups.MenuOptionGroup
: A wrapper for checkable menu items (radio and checkbox).MenuItemOption
: The checkable menu item, to be used withMenuOptionGroup
.
import {Menu,MenuButton,MenuList,MenuItem,MenuItemOption,MenuGroup,MenuOptionGroup,MenuIcon,MenuCommand,MenuDivider,} from "@chakra-ui/react"
Usage#
<Menu><MenuButton as={Button} rightIcon={<ChevronDownIcon />}>Actions</MenuButton><MenuList><MenuItem>Download</MenuItem><MenuItem>Create a Copy</MenuItem><MenuItem>Mark as Draft</MenuItem><MenuItem>Delete</MenuItem><MenuItem>Attend a Workshop</MenuItem></MenuList></Menu>
<Menu><MenuButton as={Button} rightIcon={<ChevronDownIcon />}>Actions</MenuButton><MenuList><MenuItem>Download</MenuItem><MenuItem>Create a Copy</MenuItem><MenuItem>Mark as Draft</MenuItem><MenuItem>Delete</MenuItem><MenuItem>Attend a Workshop</MenuItem></MenuList></Menu>
Accessing the internal state#
To access the internal state of the Menu
, use a function as children
(commonly known as a render prop). You'll get access to the internal state
isOpen
and method onClose
.
<Menu>{({ isOpen }) => (<><MenuButton isActive={isOpen} as={Button} rightIcon={<ChevronDownIcon />}>{isOpen ? "Close" : "Open"}</MenuButton><MenuList><MenuItem>Download</MenuItem><MenuItem onClick={() => alert("Kagebunshin")}>Create a Copy</MenuItem></MenuList></>)}</Menu>
<Menu>{({ isOpen }) => (<><MenuButton isActive={isOpen} as={Button} rightIcon={<ChevronDownIcon />}>{isOpen ? "Close" : "Open"}</MenuButton><MenuList><MenuItem>Download</MenuItem><MenuItem onClick={() => alert("Kagebunshin")}>Create a Copy</MenuItem></MenuList></>)}</Menu>
Customizing the button#
The default MenuButton
can be styled using the usual styled-system props, but
it starts off plainly styled.
Using the as
prop of the MenuButton
, you can render a custom component
instead of the default MenuButton
. For instance, you can use Chakra's Button
component, or your own custom component.
Custom components must take a
ref
prop which is assigned to the React component that triggers the menu opening. This is so that theMenuList
popover can be positioned correctly. Without this, theMenuList
will render in an undefined position.
Letter Navigation#
When focus is on the MenuButton
or within the MenuList
and you type a letter
key, a search begins. Focus will move to the first MenuItem
that starts with
the letter you typed.
Open the menu, try and type any letter, (say "S") to see the focus movement.
<Menu><MenuButtonpx={4}py={2}transition="all 0.2s"borderRadius="md"borderWidth="1px"_hover={{ bg: "gray.400" }}_expanded={{ bg: "blue.400" }}_focus={{ boxShadow: "outline" }}>File <ChevronDownIcon /></MenuButton><MenuList><MenuItem>New File</MenuItem><MenuItem>New Window</MenuItem><MenuDivider /><MenuItem>Open...</MenuItem><MenuItem>Save File</MenuItem></MenuList></Menu>
<Menu><MenuButtonpx={4}py={2}transition="all 0.2s"borderRadius="md"borderWidth="1px"_hover={{ bg: "gray.400" }}_expanded={{ bg: "blue.400" }}_focus={{ boxShadow: "outline" }}>File <ChevronDownIcon /></MenuButton><MenuList><MenuItem>New File</MenuItem><MenuItem>New Window</MenuItem><MenuDivider /><MenuItem>Open...</MenuItem><MenuItem>Save File</MenuItem></MenuList></Menu>
Just another example#
<Menu><MenuButton as={Button} rightIcon={<ChevronDownIcon />}>Your Cats</MenuButton><MenuList><MenuItem minH="48px"><ImageboxSize="2rem"borderRadius="full"src="https://placekitten.com/100/100"alt="Fluffybuns the destroyer"mr="12px"/><span>Fluffybuns the Destroyer</span></MenuItem><MenuItem minH="40px"><ImageboxSize="2rem"borderRadius="full"src="https://placekitten.com/120/120"alt="Simon the pensive"mr="12px"/><span>Simon the pensive</span></MenuItem></MenuList></Menu>
<Menu><MenuButton as={Button} rightIcon={<ChevronDownIcon />}>Your Cats</MenuButton><MenuList><MenuItem minH="48px"><ImageboxSize="2rem"borderRadius="full"src="https://placekitten.com/100/100"alt="Fluffybuns the destroyer"mr="12px"/><span>Fluffybuns the Destroyer</span></MenuItem><MenuItem minH="40px"><ImageboxSize="2rem"borderRadius="full"src="https://placekitten.com/120/120"alt="Simon the pensive"mr="12px"/><span>Simon the pensive</span></MenuItem></MenuList></Menu>
Adding icons and commands#
You can add icon to each MenuItem
by passing the icon
prop. To add a
commands (or hotkeys) to menu items, you can use the command
prop.
<Menu><MenuButtonas={IconButton}aria-label="Options"icon={<HamburgerIcon />}variant="outline"/><MenuList><MenuItem icon={<AddIcon />} command="⌘T">New Tab</MenuItem><MenuItem icon={<ExternalLinkIcon />} command="⌘N">New Window</MenuItem><MenuItem icon={<RepeatIcon />} command="⌘⇧N">Open Closed Tab</MenuItem><MenuItem icon={<EditIcon />} command="⌘O">Open File...</MenuItem></MenuList></Menu>
<Menu><MenuButtonas={IconButton}aria-label="Options"icon={<HamburgerIcon />}variant="outline"/><MenuList><MenuItem icon={<AddIcon />} command="⌘T">New Tab</MenuItem><MenuItem icon={<ExternalLinkIcon />} command="⌘N">New Window</MenuItem><MenuItem icon={<RepeatIcon />} command="⌘⇧N">Open Closed Tab</MenuItem><MenuItem icon={<EditIcon />} command="⌘O">Open File...</MenuItem></MenuList></Menu>
Lazily mounting MenuItem#
By default, the Menu
component renders all children of MenuList
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 MenuList
until that menu is
open, you can use the isLazy
prop. This is useful if your Menu
needs to be
extra performant, or make network calls on mount that should only happen when
the component is displayed.
<Menu isLazy><MenuButton>Open menu</MenuButton><MenuList>{/* MenuItems are not rendered unless Menu is open */}<MenuItem>New Window</MenuItem><MenuItem>Open Closed Tab</MenuItem><MenuItem>Open File</MenuItem></MenuList></Menu>
<Menu isLazy><MenuButton>Open menu</MenuButton><MenuList>{/* MenuItems are not rendered unless Menu is open */}<MenuItem>New Window</MenuItem><MenuItem>Open Closed Tab</MenuItem><MenuItem>Open File</MenuItem></MenuList></Menu>
Rendering menu in a portal#
To render menus in a portal, import the Portal
component and wrap the
MenuList
within the Portal
.
<Menu><MenuButton>Open menu</MenuButton><Portal><MenuList><MenuItem>Menu 1</MenuItem><MenuItem>New Window</MenuItem><MenuItem>Open Closed Tab</MenuItem><MenuItem>Open File</MenuItem></MenuList></Portal></Menu>
<Menu><MenuButton>Open menu</MenuButton><Portal><MenuList><MenuItem>Menu 1</MenuItem><MenuItem>New Window</MenuItem><MenuItem>Open Closed Tab</MenuItem><MenuItem>Open File</MenuItem></MenuList></Portal></Menu>
MenuGroup#
To group related MenuItem
s, use the MenuGroup
component and pass it a
title
for the group name.
<Menu><MenuButton as={Button} colorScheme="pink">Profile</MenuButton><MenuList><MenuGroup title="Profile"><MenuItem>My Account</MenuItem><MenuItem>Payments </MenuItem></MenuGroup><MenuDivider /><MenuGroup title="Help"><MenuItem>Docs</MenuItem><MenuItem>FAQ</MenuItem></MenuGroup></MenuList></Menu>
<Menu><MenuButton as={Button} colorScheme="pink">Profile</MenuButton><MenuList><MenuGroup title="Profile"><MenuItem>My Account</MenuItem><MenuItem>Payments </MenuItem></MenuGroup><MenuDivider /><MenuGroup title="Help"><MenuItem>Docs</MenuItem><MenuItem>FAQ</MenuItem></MenuGroup></MenuList></Menu>
Menu option groups#
You can compose a menu for table headers to help with sorting and filtering
options. Use the MenuOptionGroup
and MenuItemOption
components.
<Menu closeOnSelect={false}><MenuButton as={Button} colorScheme="brand">MenuItem</MenuButton><MenuList minWidth="240px"><MenuOptionGroup defaultValue="asc" title="Order" type="radio"><MenuItemOption value="asc">Ascending</MenuItemOption><MenuItemOption value="desc">Descending</MenuItemOption></MenuOptionGroup><MenuDivider /><MenuOptionGroup title="Country" type="checkbox"><MenuItemOption value="email">Email</MenuItemOption><MenuItemOption value="phone">Phone</MenuItemOption><MenuItemOption value="country">Country</MenuItemOption></MenuOptionGroup></MenuList></Menu>
<Menu closeOnSelect={false}><MenuButton as={Button} colorScheme="brand">MenuItem</MenuButton><MenuList minWidth="240px"><MenuOptionGroup defaultValue="asc" title="Order" type="radio"><MenuItemOption value="asc">Ascending</MenuItemOption><MenuItemOption value="desc">Descending</MenuItemOption></MenuOptionGroup><MenuDivider /><MenuOptionGroup title="Country" type="checkbox"><MenuItemOption value="email">Email</MenuItemOption><MenuItemOption value="phone">Phone</MenuItemOption><MenuItemOption value="country">Country</MenuItemOption></MenuOptionGroup></MenuList></Menu>
Accessibility#
Keyboard Interaction#
Key | Action |
---|---|
Enter or Space | When MenuButton receives focus, opens the menu and places focus on the first menu item. |
ArrowDown | When MenuButton receives focus, opens the menu and moves focus to the first menu item. |
ArrowUp | When MenuButton 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 MenuButton . |
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 MenuButton
:
role
is set tobutton
.aria-haspopup
is set tomenu
.- When the menu is displayed,
aria-expanded
is set totrue
. aria-controls
is set to theid
of theMenuList
.
For MenuList
:
role
is set tomenu
.aria-orientation
is set tovertical
.
For MenuItem
:
role
is set tomenuitem
.- Gets one of these roles
menuitem
/menuitemradio
/menuitemcheckbox
.
Props#
Menu Props#
arrowPadding
arrowPadding
number
8
autoSelect
autoSelect
If true
, the first enabled menu item will receive focus and be selected
when the menu opens.
boolean
true
boundary
boundary
The boundary area for the popper. Used within the preventOverflow
modifier
HTMLElement | "clippingParents" | "scrollParent"
"clippingParents"
closeOnBlur
closeOnBlur
If true
, the menu will close when you click outside
the menu list
boolean
true
closeOnSelect
closeOnSelect
If true
, the menu will close when a menu item is
clicked
boolean
true
colorScheme
colorScheme
Color Schemes for Menu
are not implemented in the default theme. You can extend the themeto implement them.
"brand" | "whiteAlpha" | "blackAlpha" | "gray" | "red" | "orange" | "yellow" | "green" | "teal" | "blue" | "cyan" | "purple" | "pink" | "linkedin" | "facebook" | ... 4 more ...
computePositionOnMount
computePositionOnMount
If true
, the menu will be positioned when it mounts
(even if it's not open).
Note 🚨: We don't recommend using this in a menu/popover intensive UI or page
as it might affect scrolling performance.
boolean
defaultIsOpen
defaultIsOpen
boolean
enabled
enabled
Whether the popper.js should be enabled
boolean
eventListeners
eventListeners
If provided, determines whether the popper will reposition itself on scroll
and resize
of the window.
boolean | { scroll?: boolean; resize?: boolean | undefined; } | undefined
flip
flip
If true
, the popper will change its placement and flip when it's
about to overflow its boundary area.
boolean
true
gutter
gutter
The distance or margin between the reference and popper.
It is used internally to create an offset
modifier.
NB: If you define offset
prop, it'll override the gutter.
number
8
id
id
string
isLazy
isLazy
Performance 🚀:
If true
, the MenuItem rendering will be deferred
until the menu is open.
boolean
isOpen
isOpen
boolean
lazyBehavior
lazyBehavior
Performance 🚀: The lazy behavior of menu's content when not visible. Only works when `isLazy={true}` - "unmount": The menu's content is always unmounted when not open. - "keepMounted": The menu's content initially unmounted, but stays mounted when menu is open.
"unmount" | "keepMounted"
"unmount"
matchWidth
matchWidth
If true
, the popper will match the width of the reference at all times.
It's useful for autocomplete
, `date-picker` and select
patterns.
boolean
modifiers
modifiers
Array of popper.js modifiers. Check the docs to see the list of possible modifiers you can pass. @see Docs https://popper.js.org/docs/v2/modifiers/
Partial<Modifier<string, any>>[]
offset
offset
The main and cross-axis offset to displace popper element from its reference element.
[crossAxis: number, mainAxis: number]
onClose
onClose
(() => void)
onOpen
onOpen
(() => void)
placement
placement
The placement of the popper relative to its reference.
"bottom" | "left" | "right" | "top" | "auto" | "auto-start" | "auto-end" | "top-start" | "top-end" | "bottom-start" | "bottom-end" | "right-start" | "right-end" | "left-start" | "left-end"
"bottom"
preventOverflow
preventOverflow
If true
, will prevent the popper from being cut off and ensure
it's visible within the boundary area.
boolean
true
size
size
Sizes for Menu
are not implemented in the default theme. You can extend the themeto implement them.
string
strategy
strategy
The CSS positioning strategy to use.
"fixed" | "absolute"
"absolute"
variant
variant
Variants for Menu
are not implemented in the default theme. You can extend the themeto implement them.
string
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#
closeOnSelect
closeOnSelect
Overrides the parent menu's closeOnSelect
prop.
boolean
command
command
Right-aligned label text content, useful for displaying hotkeys.
string
commandSpacing
commandSpacing
The spacing between the command and menu item's label.
SystemProps["ml"]
icon
icon
The icon to render before the menu item's label.
React.ReactElement
iconSpacing
iconSpacing
The spacing between the icon and menu item's label.
SystemProps["mr"]
isDisabled
isDisabled
If true
, the menuitem will be disabled
boolean
isFocusable
isFocusable
If true
and the menuitem is disabled, it'll
remain keyboard-focusable
boolean
MenuGroup Props#
MenuGroup
composes Box so you can
pass all Box
props to change its style.
MenuOptionGroup Props#
defaultValue
defaultValue
string | string[]
onChange
onChange
((value: string | string[]) => void)
type
type
"checkbox" | "radio"
value
value
string | string[]
MenuItemOption Props#
MenuItemOption
composes Box so you
can pass all box props in addition to these:
closeOnSelect
closeOnSelect
Overrides the parent menu's closeOnSelect
prop.
boolean
command
command
Right-aligned label text content, useful for displaying hotkeys.
string
commandSpacing
commandSpacing
The spacing between the command and menu item's label.
SystemProps["ml"]
icon
icon
The icon to render before the menu item's label.
React.ReactElement
iconSpacing
iconSpacing
The spacing between the icon and menu item's label.
SystemProps["mr"]
isChecked
isChecked
boolean
isDisabled
isDisabled
If true
, the menuitem will be disabled
boolean
isFocusable
isFocusable
If true
and the menuitem is disabled, it'll
remain keyboard-focusable
boolean
type
type
"checkbox" | "radio"
value
value
string