import { Button } from "@/components/ui/button"
import {
MenuContent,
MenuItem,
MenuRoot,
MenuTrigger,
} from "@/components/ui/menu"
const Demo = () => {
return (
<MenuRoot>
<MenuTrigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</MenuTrigger>
<MenuContent>
<MenuItem value="new-txt">New Text File</MenuItem>
<MenuItem value="new-file">New File...</MenuItem>
<MenuItem value="new-win">New Window</MenuItem>
<MenuItem value="open-file">Open File...</MenuItem>
<MenuItem value="export">Export</MenuItem>
</MenuContent>
</MenuRoot>
)
}
Setup
If you don't already have the snippet, run the following command to add the
menu
snippet
npx @chakra-ui/cli snippet add menu
The snippet includes a closed component composition for the Menu
component.
Usage
import {
MenuContent,
MenuItem,
MenuRoot,
MenuTrigger,
} from "@/components/ui/menu"
<MenuRoot>
<MenuTrigger />
<MenuContent>
<MenuItem value="..." />
</MenuContent>
</MenuRoot>
Examples
Command
Use the MenuItemCommand
component to display a command in the menu.
import { Button } from "@/components/ui/button"
import {
MenuContent,
MenuItem,
MenuItemCommand,
MenuRoot,
MenuTrigger,
} from "@/components/ui/menu"
const Demo = () => {
return (
<MenuRoot>
<MenuTrigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</MenuTrigger>
<MenuContent>
<MenuItem value="new-txt-a">
New Text File <MenuItemCommand>⌘E</MenuItemCommand>
</MenuItem>
<MenuItem value="new-file-a">
New File... <MenuItemCommand>⌘N</MenuItemCommand>
</MenuItem>
<MenuItem value="new-win-a">
New Window <MenuItemCommand>⌘⇧N</MenuItemCommand>
</MenuItem>
<MenuItem value="open-file-a">
Open File... <MenuItemCommand>⌘O</MenuItemCommand>
</MenuItem>
<MenuItem value="export-a">
Export <MenuItemCommand>⌘S</MenuItemCommand>
</MenuItem>
</MenuContent>
</MenuRoot>
)
}
Context menu
Use the MenuContextTrigger
component to create a context menu.
import { Center } from "@chakra-ui/react"
import {
MenuContent,
MenuContextTrigger,
MenuItem,
MenuRoot,
} from "@/components/ui/menu"
const Demo = () => {
return (
<MenuRoot>
<MenuContextTrigger w="full">
<Center
width="full"
height="40"
userSelect="none"
borderWidth="2px"
borderStyle="dashed"
rounded="lg"
padding="4"
>
Right click here
</Center>
</MenuContextTrigger>
<MenuContent>
<MenuItem value="new-txt">New Text File</MenuItem>
<MenuItem value="new-file">New File...</MenuItem>
<MenuItem value="new-win">New Window</MenuItem>
<MenuItem value="open-file">Open File...</MenuItem>
<MenuItem value="export">Export</MenuItem>
</MenuContent>
</MenuRoot>
)
}
Group
Use the MenuItemGroup
component to group related menu items.
import { Button } from "@/components/ui/button"
import {
MenuContent,
MenuItem,
MenuItemGroup,
MenuRoot,
MenuSeparator,
MenuTrigger,
} from "@/components/ui/menu"
const Demo = () => {
return (
<MenuRoot>
<MenuTrigger asChild>
<Button variant="outline">Edit</Button>
</MenuTrigger>
<MenuContent>
<MenuItemGroup title="Styles">
<MenuItem value="bold">Bold</MenuItem>
<MenuItem value="underline">Underline</MenuItem>
</MenuItemGroup>
<MenuSeparator />
<MenuItemGroup title="Align">
<MenuItem value="left">Left</MenuItem>
<MenuItem value="middle">Middle</MenuItem>
<MenuItem value="right">Right</MenuItem>
</MenuItemGroup>
</MenuContent>
</MenuRoot>
)
}
Danger Item
Here's an example of how to style a menu item that is used to delete an item.
import { Button } from "@/components/ui/button"
import {
MenuContent,
MenuItem,
MenuRoot,
MenuTrigger,
} from "@/components/ui/menu"
const Demo = () => {
return (
<MenuRoot>
<MenuTrigger asChild>
<Button variant="outline" size="sm">
Open Menu
</Button>
</MenuTrigger>
<MenuContent>
<MenuItem value="rename">Rename</MenuItem>
<MenuItem value="export">Export</MenuItem>
<MenuItem
value="delete"
color="fg.error"
_hover={{ bg: "bg.error", color: "fg.error" }}
>
Delete...
</MenuItem>
</MenuContent>
</MenuRoot>
)
}
Submenu
Here's an example of how to create a submenu.
import { Button } from "@/components/ui/button"
import {
MenuContent,
MenuItem,
MenuRoot,
MenuTrigger,
MenuTriggerItem,
} from "@/components/ui/menu"
const Demo = () => {
return (
<MenuRoot>
<MenuTrigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</MenuTrigger>
<MenuContent>
<MenuItem value="new-txt">New Text File</MenuItem>
<MenuItem value="new-file">New File...</MenuItem>
<MenuRoot positioning={{ placement: "right-start", gutter: 2 }}>
<MenuTriggerItem value="open-recent">Open Recent</MenuTriggerItem>
<MenuContent>
<MenuItem value="panda">Panda</MenuItem>
<MenuItem value="ark">Ark UI</MenuItem>
<MenuItem value="chakra">Chakra v3</MenuItem>
</MenuContent>
</MenuRoot>
<MenuItem value="open-file">Open File...</MenuItem>
<MenuItem value="export">Export</MenuItem>
</MenuContent>
</MenuRoot>
)
}
Links
Pass the asChild
prop to the MenuItem
component to render a link.
import { Button } from "@/components/ui/button"
import {
MenuContent,
MenuItem,
MenuRoot,
MenuTrigger,
} from "@/components/ui/menu"
const Demo = () => {
return (
<MenuRoot>
<MenuTrigger asChild>
<Button size="sm" variant="outline">
Select Anime
</Button>
</MenuTrigger>
<MenuContent>
<MenuItem asChild value="naruto">
<a
href="https://www.crunchyroll.com/naruto"
target="_blank"
rel="noreferrer"
>
Naruto
</a>
</MenuItem>
<MenuItem asChild value="one-piece">
<a
href="https://www.crunchyroll.com/one-piece"
target="_blank"
rel="noreferrer"
>
One Piece
</a>
</MenuItem>
<MenuItem asChild value="attack-on-titan">
<a
href="https://www.crunchyroll.com/attack-on-titan"
target="_blank"
rel="noreferrer"
>
Attack on Titan
</a>
</MenuItem>
</MenuContent>
</MenuRoot>
)
}
When using custom router links, you need to set the navigate
prop on the
MenuRoot
component.
"use client"
import { Menu } from "@/components/ui/menu"
import { useNavigate } from "react-router-dom"
const Demo = () => {
const navigate = useNavigate()
return (
<MenuRoot navigate={({ value, node }) => navigate(`/${value}`)}>
{/* ... */}
</MenuRoot>
)
}
With Radio
Here's an example of how to create a menu with radio.
"use client"
import { Button } from "@/components/ui/button"
import {
MenuContent,
MenuRadioItem,
MenuRadioItemGroup,
MenuRoot,
MenuTrigger,
} from "@/components/ui/menu"
import { useState } from "react"
import { HiSortAscending } from "react-icons/hi"
const Demo = () => {
const [value, setValue] = useState("asc")
return (
<MenuRoot>
<MenuTrigger asChild>
<Button variant="outline" size="sm">
<HiSortAscending /> Sort
</Button>
</MenuTrigger>
<MenuContent minW="10rem">
<MenuRadioItemGroup
value={value}
onValueChange={(e) => setValue(e.value)}
>
<MenuRadioItem value="asc">Ascending</MenuRadioItem>
<MenuRadioItem value="desc">Descending</MenuRadioItem>
</MenuRadioItemGroup>
</MenuContent>
</MenuRoot>
)
}
Icon and Command
Compose the menu to include icons and commands.
import { Box } from "@chakra-ui/react"
import { Button } from "@/components/ui/button"
import {
MenuContent,
MenuItem,
MenuItemCommand,
MenuRoot,
MenuTrigger,
} from "@/components/ui/menu"
import { LuClipboardPaste, LuCopy, LuScissors } from "react-icons/lu"
const Demo = () => {
return (
<MenuRoot>
<MenuTrigger asChild>
<Button variant="outline">Edit</Button>
</MenuTrigger>
<MenuContent>
<MenuItem value="cut" valueText="cut">
<LuScissors />
<Box flex="1">Cut</Box>
<MenuItemCommand>⌘X</MenuItemCommand>
</MenuItem>
<MenuItem value="copy" valueText="copy">
<LuCopy />
<Box flex="1">Copy</Box>
<MenuItemCommand>⌘C</MenuItemCommand>
</MenuItem>
<MenuItem value="paste" valueText="paste">
<LuClipboardPaste />
<Box flex="1">Paste</Box>
<MenuItemCommand>⌘V</MenuItemCommand>
</MenuItem>
</MenuContent>
</MenuRoot>
)
}
Placement
Use the positioning.placement
prop to control the placement of the menu.
import { Button } from "@/components/ui/button"
import {
MenuContent,
MenuItem,
MenuRoot,
MenuTrigger,
} from "@/components/ui/menu"
const Demo = () => {
return (
<MenuRoot positioning={{ placement: "right-start" }}>
<MenuTrigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</MenuTrigger>
<MenuContent>
<MenuItem value="new-txt">New Text File</MenuItem>
<MenuItem value="new-file">New File...</MenuItem>
<MenuItem value="new-win">New Window</MenuItem>
<MenuItem value="open-file">Open File...</MenuItem>
<MenuItem value="export">Export</MenuItem>
</MenuContent>
</MenuRoot>
)
}
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, Group } from "@chakra-ui/react"
import { Button } from "@/components/ui/button"
import {
MenuContent,
MenuItem,
MenuRoot,
MenuTrigger,
} from "@/components/ui/menu"
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 (
<MenuRoot>
<MenuTrigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</MenuTrigger>
<MenuContent>
<Group grow gap="0">
{horizontalMenuItems.map((item) => (
<MenuItem
key={item.value}
value={item.value}
width="14"
gap="1"
flexDirection="column"
justifyContent="center"
>
{item.icon}
{item.label}
</MenuItem>
))}
</Group>
{verticalMenuItems.map((item) => (
<MenuItem key={item.value} value={item.value}>
<Box flex="1">{item.label}</Box>
{item.icon}
</MenuItem>
))}
</MenuContent>
</MenuRoot>
)
}
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, Text } from "@chakra-ui/react"
import {
MenuContent,
MenuItem,
MenuRoot,
MenuTrigger,
} from "@/components/ui/menu"
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>
<MenuRoot positioning={{ hideWhenDetached: true }}>
<MenuTrigger asChild>
<Box as="button" bg="green.100" p="4" borderRadius="md">
Menu
</Box>
</MenuTrigger>
<MenuContent>
<MenuItem value="new-txt">New Text File</MenuItem>
<MenuItem value="new-file">New File...</MenuItem>
<MenuItem value="new-win">New Window</MenuItem>
<MenuItem value="open-file">Open File...</MenuItem>
<MenuItem value="export">Export</MenuItem>
</MenuContent>
</MenuRoot>
</Box>
</Flex>
</Center>
)
}
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' | 'accent' 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. |