import { Button, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt">New Text File</Menu.Item>
<Menu.Item value="new-file">New File...</Menu.Item>
<Menu.Item value="new-win">New Window</Menu.Item>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
Usage
import { Menu } from "@chakra-ui/react"<Menu.Root>
<Menu.Trigger />
<Menu.Positioner>
<Menu.Content>
<Menu.Item />
<Menu.ItemGroup>
<Menu.Item />
</Menu.ItemGroup>
<Menu.Separator />
<Menu.Arrow />
<Menu.CheckboxItem>
<Menu.ItemIndicator />
</Menu.CheckboxItem>
<Menu.RadioItemGroup>
<Menu.RadioItem>
<Menu.ItemIndicator />
</Menu.RadioItem>
</Menu.RadioItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>Examples
Command
Use the Menu.ItemCommand component to display a command in the menu.
import { Button, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt-a">
New Text File <Menu.ItemCommand>⌘E</Menu.ItemCommand>
</Menu.Item>
<Menu.Item value="new-file-a">
New File... <Menu.ItemCommand>⌘N</Menu.ItemCommand>
</Menu.Item>
<Menu.Item value="new-win-a">
New Window <Menu.ItemCommand>⌘W</Menu.ItemCommand>
</Menu.Item>
<Menu.Item value="open-file-a">
Open File... <Menu.ItemCommand>⌘O</Menu.ItemCommand>
</Menu.Item>
<Menu.Item value="export-a">
Export <Menu.ItemCommand>⌘S</Menu.ItemCommand>
</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
Context menu
Use the Menu.ContextTrigger component to create a context menu.
import { Center, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root>
<Menu.ContextTrigger width="full">
<Center
height="40"
userSelect="none"
borderWidth="2px"
borderStyle="dashed"
rounded="lg"
padding="4"
>
Right click here
</Center>
</Menu.ContextTrigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt">New Text File</Menu.Item>
<Menu.Item value="new-file">New File...</Menu.Item>
<Menu.Item value="new-win">New Window</Menu.Item>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
Group
Use the Menu.ItemGroup component to group related menu items.
import { Button, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline">Edit</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.ItemGroup>
<Menu.ItemGroupLabel>Styles</Menu.ItemGroupLabel>
<Menu.Item value="bold">Bold</Menu.Item>
<Menu.Item value="underline">Underline</Menu.Item>
</Menu.ItemGroup>
<Menu.Separator />
<Menu.ItemGroup>
<Menu.ItemGroupLabel>Align</Menu.ItemGroupLabel>
<Menu.Item value="left">Left</Menu.Item>
<Menu.Item value="middle">Middle</Menu.Item>
<Menu.Item value="right">Right</Menu.Item>
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
Danger Item
Here's an example of how to style a menu item that is used to delete an item.
import { Button, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open Menu
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="rename">Rename</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
<Menu.Item
value="delete"
color="fg.error"
_hover={{ bg: "bg.error", color: "fg.error" }}
>
Delete...
</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
Submenu
Here's an example of how to create a submenu.
import { Button, Menu, Portal } from "@chakra-ui/react"
import { LuChevronRight } from "react-icons/lu"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt">New Text File</Menu.Item>
<Menu.Item value="new-file">New File...</Menu.Item>
<Menu.Root positioning={{ placement: "right-start", gutter: 2 }}>
<Menu.TriggerItem>
Open Recent <LuChevronRight />
</Menu.TriggerItem>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="panda">Panda</Menu.Item>
<Menu.Item value="ark">Ark UI</Menu.Item>
<Menu.Item value="chakra">Chakra v3</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
Links
Pass the asChild prop to the Menu.Item component to render a link.
import { Button, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button size="sm" variant="outline">
Select Anime
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
{links.map((link) => (
<Menu.Item key={link.href} asChild value={link.title}>
<a href={link.href} target="_blank" rel="noreferrer">
{link.title}
</a>
</Menu.Item>
))}
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
const links = [
{
title: "Naruto",
href: "https://www.crunchyroll.com/naruto",
},
{
title: "One Piece",
href: "https://www.crunchyroll.com/one-piece",
},
{
title: "Attack on Titan",
href: "https://www.crunchyroll.com/attack-on-titan",
},
]
When using custom router links, you need to set the navigate prop on the
Menu.Root component.
"use client"
import { Menu } from "@chakra-ui/react"
import { useNavigate } from "react-router-dom"
const Demo = () => {
const navigate = useNavigate()
return (
<Menu.Root navigate={({ value, node }) => navigate(`/${value}`)}>
{/* ... */}
</Menu.Root>
)
}Radio Items
Here's an example of how to create a menu with radio items.
"use client"
import { Button, Menu, Portal } from "@chakra-ui/react"
import { useState } from "react"
import { HiSortAscending } from "react-icons/hi"
const Demo = () => {
const [value, setValue] = useState("asc")
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
<HiSortAscending /> Sort
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content minW="10rem">
<Menu.RadioItemGroup
value={value}
onValueChange={(e) => setValue(e.value)}
>
{items.map((item) => (
<Menu.RadioItem key={item.value} value={item.value}>
{item.label}
<Menu.ItemIndicator />
</Menu.RadioItem>
))}
</Menu.RadioItemGroup>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
const items = [
{ label: "Ascending", value: "asc" },
{ label: "Descending", value: "desc" },
]
Checkbox Items
Here's an example of how to create a menu with checkbox items.
"use client"
import { Button, Menu, Portal, useCheckboxGroup } from "@chakra-ui/react"
import { HiCog } from "react-icons/hi"
const Demo = () => {
const group = useCheckboxGroup({ defaultValue: ["autosave"] })
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
<HiCog /> Features
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.ItemGroup>
<Menu.ItemGroupLabel>Features</Menu.ItemGroupLabel>
{items.map(({ title, value }) => (
<Menu.CheckboxItem
key={value}
value={value}
checked={group.isChecked(value)}
onCheckedChange={() => group.toggleValue(value)}
>
{title}
<Menu.ItemIndicator />
</Menu.CheckboxItem>
))}
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
const items = [
{ title: "Autosave", value: "autosave" },
{ title: "Detect Language", value: "detect-language" },
{ title: "Spellcheck", value: "spellcheck" },
]
Icon and Command
Compose the menu to include icons and commands.
import { Box, Button, Menu, Portal } from "@chakra-ui/react"
import { LuClipboardPaste, LuCopy, LuScissors } from "react-icons/lu"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline">Edit</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="cut">
<LuScissors />
<Box flex="1">Cut</Box>
<Menu.ItemCommand>⌘X</Menu.ItemCommand>
</Menu.Item>
<Menu.Item value="copy">
<LuCopy />
<Box flex="1">Copy</Box>
<Menu.ItemCommand>⌘C</Menu.ItemCommand>
</Menu.Item>
<Menu.Item value="paste">
<LuClipboardPaste />
<Box flex="1">Paste</Box>
<Menu.ItemCommand>⌘V</Menu.ItemCommand>
</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
Placement
Use the positioning.placement prop to control the placement of the menu.
import { Button, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root positioning={{ placement: "right-start" }}>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt">New Text File</Menu.Item>
<Menu.Item value="new-file">New File...</Menu.Item>
<Menu.Item value="new-win">New Window</Menu.Item>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
Avatar
Here's an example that composes the Menu with the Avatar component to
display a menu underneath an avatar.
import { Avatar, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root positioning={{ placement: "right-end" }}>
<Menu.Trigger rounded="full" focusRing="outside">
<Avatar.Root size="sm">
<Avatar.Fallback name="Segun Adebayo" />
<Avatar.Image src="https://bit.ly/sage-adebayo" />
</Avatar.Root>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="account">Account</Menu.Item>
<Menu.Item value="settings">Settings</Menu.Item>
<Menu.Item value="logout">Logout</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
Anchor Point
Use the positioning.anchorPoint prop to control the anchor point of the menu.
You can derive it from the getBoundingClientRect of a DOM element, or use
something like DOMRect.fromRect({ x: 0, y: 0, width: 1, height: 1 }) to create
a new rect.
"use client"
import { Box, Button, Menu, Portal } from "@chakra-ui/react"
import { useRef } from "react"
const Demo = () => {
const ref = useRef<HTMLDivElement | null>(null)
const getAnchorRect = () => ref.current!.getBoundingClientRect()
return (
<Menu.Root positioning={{ getAnchorRect }}>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<Box layerStyle="fill.subtle" p="4" ref={ref} mt="4">
Anchor
</Box>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt">New Text File</Menu.Item>
<Menu.Item value="new-file">New File...</Menu.Item>
<Menu.Item value="new-win">New Window</Menu.Item>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
Mixed Layout
Here's an example of how to create a mixed layout of menu items. In this layout, the top horizontal menu includes common menu items.
import { Box, Button, Group, Menu, Portal } from "@chakra-ui/react"
import {
LuClipboard,
LuCopy,
LuFileSearch,
LuMessageSquare,
LuScissors,
LuShare,
} from "react-icons/lu"
const horizontalMenuItems = [
{ label: "Cut", value: "cut", icon: <LuScissors /> },
{ label: "Copy", value: "copy", icon: <LuCopy /> },
{ label: "Paste", value: "paste", icon: <LuClipboard /> },
]
const verticalMenuItems = [
{ label: "Look Up", value: "look-up", icon: <LuFileSearch /> },
{ label: "Translate", value: "translate", icon: <LuMessageSquare /> },
{ label: "Share", value: "share", icon: <LuShare /> },
]
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Group grow gap="0">
{horizontalMenuItems.map((item) => (
<Menu.Item
key={item.value}
value={item.value}
width="14"
gap="1"
flexDirection="column"
justifyContent="center"
>
{item.icon}
{item.label}
</Menu.Item>
))}
</Group>
{verticalMenuItems.map((item) => (
<Menu.Item key={item.value} value={item.value}>
<Box flex="1">{item.label}</Box>
{item.icon}
</Menu.Item>
))}
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
Overflow
When you have a long list of menu items, you can set a maxH prop on the
Menu.Content to create a scrollable menu.
maxHeight: "var(--available-height)", which is the
maximum available height for the content relative to the viewport.import { Button, Menu, Portal } from "@chakra-ui/react"
const Demo = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open Menu
</Button>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content maxH="200px" minW="10rem">
{menuItems.map((item) => (
<Menu.Item key={item.value} value={item.value}>
{item.label}
</Menu.Item>
))}
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
)
}
const menuItems = [
{ value: "new-file", label: "New File" },
{ value: "new-folder", label: "New Folder" },
{ value: "open", label: "Open..." },
{ value: "open-recent", label: "Open Recent" },
{ value: "save", label: "Save" },
{ value: "save-as", label: "Save As..." },
{ value: "save-all", label: "Save All" },
{ value: "export", label: "Export" },
{ value: "import", label: "Import" },
{ value: "print", label: "Print" },
{ value: "share", label: "Share" },
{ value: "duplicate", label: "Duplicate" },
{ value: "rename", label: "Rename" },
{ value: "move", label: "Move To..." },
{ value: "copy", label: "Copy To..." },
{ value: "delete", label: "Delete" },
{ value: "find", label: "Find" },
{ value: "replace", label: "Replace" },
{ value: "preferences", label: "Preferences" },
{ value: "settings", label: "Settings" },
{ value: "help", label: "Help" },
{ value: "about", label: "About" },
{ value: "quit", label: "Quit" },
]
Hide When Detached
When the menu is rendered in an scrolling container, set the
positioning.hideWhenDetached to true to hide the menu when the trigger is
scrolled out of view.
Item0
Item1
Item2
Item3
Item4
Item5
import { Box, Center, Flex, Menu, Portal, Text } from "@chakra-ui/react"
const Demo = () => {
return (
<Center minH="sm">
<Flex
w="300px"
h="full"
overflowX="auto"
gapX="6"
p="4"
borderWidth="1px"
bg="bg.subtle"
>
{[...Array(6).keys()].map((x) => (
<Box layerStyle="fill.surface" p="4" borderRadius="md" key={x}>
<Text>Item{x}</Text>
</Box>
))}
<Box>
<Menu.Root positioning={{ hideWhenDetached: true }}>
<Menu.Trigger asChild>
<Box as="button" bg="green.100" p="4" borderRadius="md">
Menu
</Box>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt">New Text File</Menu.Item>
<Menu.Item value="new-file">New File...</Menu.Item>
<Menu.Item value="new-win">New Window</Menu.Item>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
</Box>
</Flex>
</Center>
)
}
Within Dialog
To use the Menu within a Dialog, you need to avoid portalling the
Menu.Positioner to the document's body.
-<Portal>
<Menu.Positioner>
<Menu.Content>
{/* ... */}
</Menu.Content>
</Menu.Positioner>
-</Portal>If you have set scrollBehavior="inside" on the Dialog, you need to:
- Set the menu positioning to
fixedto avoid the menu from being clipped by the dialog. - Set
hideWhenDetachedtotrueto hide the menu when the trigger is scrolled out of view.
<Menu.Root positioning={{ strategy: "fixed", hideWhenDetached: true }}>
{/* ... */}
</Menu.Root>"use client"
import { Button, Dialog, Menu, Portal } from "@chakra-ui/react"
import Lorem from "react-lorem-ipsum"
const Demo = () => {
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Dialog.Trigger>
<Portal>
<Dialog.Backdrop />
<Dialog.Positioner>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Welcome to the menu</Dialog.Title>
</Dialog.Header>
<Dialog.Body spaceY="4">
<DialogMenu />
<Lorem p={1} />
</Dialog.Body>
</Dialog.Content>
</Dialog.Positioner>
</Portal>
</Dialog.Root>
)
}
const DialogMenu = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Menu
</Button>
</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="new-txt">New Text File</Menu.Item>
<Menu.Item value="new-file">New File...</Menu.Item>
<Menu.Item value="new-win">New Window</Menu.Item>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
}
Props
Root
| Prop | Default | Type |
|---|---|---|
closeOnSelect | true | booleanWhether to close the menu when an option is selected |
composite | true | booleanWhether the menu is a composed with other composite widgets like a combobox or tabs |
lazyMount | false | booleanWhether to enable lazy mounting |
loopFocus | false | booleanWhether to loop the keyboard navigation. |
skipAnimationOnMount | false | booleanWhether to allow the initial presence animation. |
typeahead | true | booleanWhether the pressing printable characters should trigger typeahead navigation |
unmountOnExit | false | booleanWhether to unmount on exit. |
colorPalette | 'gray' | 'gray' | 'red' | 'orange' | 'yellow' | 'green' | 'teal' | 'blue' | 'cyan' | 'purple' | 'pink'The color palette of the component |
variant | 'subtle' | 'subtle' | 'solid'The variant of the component |
size | 'md' | 'sm' | 'md'The size of the component |
as | React.ElementTypeThe underlying element to render. | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
unstyled | booleanWhether to remove the component's style. | |
anchorPoint | PointThe positioning point for the menu. Can be set by the context menu trigger or the button trigger. | |
aria-label | stringThe accessibility label for the menu | |
defaultHighlightedValue | stringThe initial highlighted value of the menu item when rendered. Use when you don't need to control the highlighted value of the menu item. | |
defaultOpen | booleanThe initial open state of the menu when rendered. Use when you don't need to control the open state of the menu. | |
highlightedValue | stringThe controlled highlighted value of the menu item. | |
id | stringThe unique identifier of the machine. | |
ids | Partial<{
trigger: string
contextTrigger: string
content: string
groupLabel: (id: string) => string
group: (id: string) => string
positioner: string
arrow: string
}>The ids of the elements in the menu. Useful for composition. | |
immediate | booleanWhether to synchronize the present change immediately or defer it to the next frame | |
navigate | (details: NavigateDetails) => voidFunction to navigate to the selected item if it's an anchor element | |
onEscapeKeyDown | (event: KeyboardEvent) => voidFunction called when the escape key is pressed | |
onExitComplete | VoidFunctionFunction called when the animation ends in the closed state | |
onFocusOutside | (event: FocusOutsideEvent) => voidFunction called when the focus is moved outside the component | |
onHighlightChange | (details: HighlightChangeDetails) => voidFunction called when the highlighted menu item changes. | |
onInteractOutside | (event: InteractOutsideEvent) => voidFunction called when an interaction happens outside the component | |
onOpenChange | (details: OpenChangeDetails) => voidFunction called when the menu opens or closes | |
onPointerDownOutside | (event: PointerDownOutsideEvent) => voidFunction called when the pointer is pressed down outside the component | |
onSelect | (details: SelectionDetails) => voidFunction called when a menu item is selected. | |
open | booleanThe controlled open state of the menu | |
positioning | PositioningOptionsThe options used to dynamically position the menu | |
present | booleanWhether the node is present (controlled by the user) |
Item
| Prop | Default | Type |
|---|---|---|
value * | stringThe unique value of the menu item option. | |
as | React.ElementTypeThe underlying element to render. | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
closeOnSelect | booleanWhether the menu should be closed when the option is selected. | |
disabled | booleanWhether the menu item is disabled | |
onSelect | VoidFunctionThe function to call when the item is selected | |
valueText | stringThe textual value of the option. Used in typeahead navigation of the menu. If not provided, the text content of the menu item will be used. |
Explorer
Explore the Menu component parts interactively. Click on parts in the sidebar
to highlight them in the preview.
Component Anatomy
Hover to highlight, click to select parts
arrow
arrowTip
content
contextTrigger
indicator
item
itemGroup
itemGroupLabel
itemIndicator
itemText
positioner
separator
trigger
triggerItem
itemCommand
menu.recipe.ts