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>
)
}
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>
)
}
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
Here's an example of how to use the menu within a Dialog
or Drawer
component.
Due to the focus trap within the dialog, it's important to change the portal target from the document's body to the dialog's content.
Alternatively, you can avoid portalling the Menu.Positioner
"use client"
import { Button, Dialog, Menu, Portal } from "@chakra-ui/react"
import { useRef } from "react"
import Lorem from "react-lorem-ipsum"
const Demo = () => {
const contentRef = useRef<HTMLDivElement | null>(null)
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Dialog.Trigger>
<Portal>
<Dialog.Backdrop />
<Dialog.Positioner>
<Dialog.Content ref={contentRef}>
<Dialog.Header>
<Dialog.Title>Welcome to the menu</Dialog.Title>
</Dialog.Header>
<Dialog.Body spaceY="4">
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Menu
</Button>
</Menu.Trigger>
<Portal container={contentRef}>
<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>
<Lorem p={1} />
</Dialog.Body>
</Dialog.Content>
</Dialog.Positioner>
</Portal>
</Dialog.Root>
)
}
Props
Root
Prop | Default | Type |
---|---|---|
closeOnSelect | true | boolean Whether to close the menu when an option is selected |
composite | true | boolean Whether the menu is a composed with other composite widgets like a combobox or tabs |
lazyMount | false | boolean Whether to enable lazy mounting |
loopFocus | false | boolean Whether to loop the keyboard navigation. |
typeahead | true | boolean Whether the pressing printable characters should trigger typeahead navigation |
unmountOnExit | false | boolean Whether 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 |
anchorPoint | Point The positioning point for the menu. Can be set by the context menu trigger or the button trigger. | |
aria-label | string The accessibility label for the menu | |
defaultOpen | boolean The initial open state of the menu when it is first rendered. Use when you do not need to control its open state. | |
highlightedValue | string The value of the highlighted menu item. | |
id | string The 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 | boolean Whether to synchronize the present change immediately or defer it to the next frame | |
navigate | (details: NavigateDetails) => void Function to navigate to the selected item if it's an anchor element | |
onEscapeKeyDown | (event: KeyboardEvent) => void Function called when the escape key is pressed | |
onExitComplete | () => void Function called when the animation ends in the closed state | |
onFocusOutside | (event: FocusOutsideEvent) => void Function called when the focus is moved outside the component | |
onHighlightChange | (details: HighlightChangeDetails) => void Function called when the highlighted menu item changes. | |
onInteractOutside | (event: InteractOutsideEvent) => void Function called when an interaction happens outside the component | |
onOpenChange | (details: OpenChangeDetails) => void Function called when the menu opens or closes | |
onPointerDownOutside | (event: PointerDownOutsideEvent) => void Function called when the pointer is pressed down outside the component | |
onSelect | (details: SelectionDetails) => void Function called when a menu item is selected. | |
open | boolean Whether the menu is open | |
positioning | PositioningOptions The options used to dynamically position the menu | |
present | boolean Whether the node is present (controlled by the user) | |
as | React.ElementType The underlying element to render. | |
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
unstyled | boolean Whether to remove the component's style. |
Item
Prop | Default | Type |
---|---|---|
value * | string The unique value of the menu item option. | |
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
closeOnSelect | boolean Whether the menu should be closed when the option is selected. | |
disabled | boolean Whether the menu item is disabled | |
valueText | string The 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. |