'use client'
import { Button, Menu } from '@saas-ui/react'
export const MenuBasic = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<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.Root>
)
}
Anatomy
import { Menu } from '@saas-ui/react/menu'
<Menu.Root>
<Menu.Button />
<Menu.Content>
<Menu.Item value="item" />
</Menu.Content>
</Menu.Root>
Examples
Sizes
Use the size
prop to control the size of the menu. The default size is md
.
'use client'
import { HStack, Menu } from '@saas-ui/react'
export const MenuSizes = () => {
return (
<HStack>
<Menu.Root size="sm">
<Menu.Button>Open</Menu.Button>
<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.Root>
<Menu.Root size="md">
<Menu.Button>Open</Menu.Button>
<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.Root>
</HStack>
)
}
Command
Use the MenuItemCommand
component to display a command in the menu.
'use client'
import { Button, Menu } from '@saas-ui/react'
export const MenuWithCommand = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<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>⌘⇧N</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.Root>
)
}
Context menu
Use the MenuContextTrigger
component to create a context menu.
'use client'
import { Center } from '@chakra-ui/react'
import { Menu } from '@saas-ui/react'
export const MenuWithContextTrigger = () => {
return (
<Menu.Root>
<Menu.ContextTrigger w="full">
<Center
width="full"
height="40"
userSelect="none"
borderWidth="2px"
borderStyle="dashed"
rounded="lg"
padding="4"
>
Right click here
</Center>
</Menu.ContextTrigger>
<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.Root>
)
}
Group
Use the MenuItemGroup
component to group related menu items.
'use client'
import { Button, Menu } from '@saas-ui/react'
export const MenuWithGroup = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline">Edit</Button>
</Menu.Trigger>
<Menu.Content>
<Menu.ItemGroup title="Styles">
<Menu.Item value="bold">Bold</Menu.Item>
<Menu.Item value="underline">Underline</Menu.Item>
</Menu.ItemGroup>
<Menu.Separator />
<Menu.ItemGroup title="Align">
<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.Root>
)
}
Submenu
Here's an example of how to create a submenu.
'use client'
import { Button, Menu } from '@saas-ui/react'
export const MenuWithSubmenu = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<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 value="open-recent">Open Recent</Menu.TriggerItem>
<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.Root>
<Menu.Item value="open-file">Open File...</Menu.Item>
<Menu.Item value="export">Export</Menu.Item>
</Menu.Content>
</Menu.Root>
)
}
Links
Pass the asChild
prop to the MenuItem
component to render a link.
'use client'
import { Button, Menu } from '@saas-ui/react'
export const MenuWithLinks = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button size="sm" variant="outline">
Select Anime
</Button>
</Menu.Trigger>
<Menu.Content>
<Menu.Item asChild value="naruto">
<a
href="https://www.crunchyroll.com/naruto"
target="_blank"
rel="noreferrer"
>
Naruto
</a>
</Menu.Item>
<Menu.Item asChild value="one-piece">
<a
href="https://www.crunchyroll.com/one-piece"
target="_blank"
rel="noreferrer"
>
One Piece
</a>
</Menu.Item>
<Menu.Item asChild value="attack-on-titan">
<a
href="https://www.crunchyroll.com/attack-on-titan"
target="_blank"
rel="noreferrer"
>
Attack on Titan
</a>
</Menu.Item>
</Menu.Content>
</Menu.Root>
)
}
With Radio
Here's an example of how to create a menu with radio.
'use client'
import { useState } from 'react'
import { Button, Menu } from '@saas-ui/react'
import { HiSortAscending } from 'react-icons/hi'
export const MenuWithRadioItems = () => {
const [value, setValue] = useState('asc')
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
<HiSortAscending /> Sort
</Button>
</Menu.Trigger>
<Menu.Content minW="10rem">
<Menu.RadioItemGroup
value={value}
onValueChange={(e) => setValue(e.value)}
>
<Menu.RadioItem value="asc" startElement={<Menu.ItemIndicator />}>
Ascending
</Menu.RadioItem>
<Menu.RadioItem value="desc" startElement={<Menu.ItemIndicator />}>
Descending
</Menu.RadioItem>
</Menu.RadioItemGroup>
</Menu.Content>
</Menu.Root>
)
}
With Checkbox
Here's an example of how to create a menu with checkbox.
'use client'
import { useState } from 'react'
import { Button, Menu } from '@saas-ui/react'
import { HiFilter } from 'react-icons/hi'
export const MenuWithCheckboxItems = () => {
const [values, setValues] = useState({
open: false,
closed: false,
})
return (
<Menu.Root closeOnSelect={false}>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
<HiFilter /> Status
</Button>
</Menu.Trigger>
<Menu.Content minW="10rem">
<Menu.CheckboxItem
checked={values.open}
onCheckedChange={(checked) => setValues({ ...values, open: checked })}
value="open"
startElement={<Menu.ItemIndicator />}
>
Open
</Menu.CheckboxItem>
<Menu.CheckboxItem
checked={values.closed}
onCheckedChange={(checked) =>
setValues({ ...values, closed: checked })
}
value="closed"
startElement={<Menu.ItemIndicator />}
>
Closed
</Menu.CheckboxItem>
</Menu.Content>
</Menu.Root>
)
}
Icon and Command
Compose the menu to include icons and commands.
'use client'
import { Box } from '@chakra-ui/react'
import { Button, Menu } from '@saas-ui/react'
import { LuClipboardPaste, LuCopy, LuScissors } from 'react-icons/lu'
export const MenuWithIconAndCommand = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline">Edit</Button>
</Menu.Trigger>
<Menu.Content>
<Menu.Item value="cut" valueText="cut">
<LuScissors />
<Box flex="1">Cut</Box>
<Menu.ItemCommand>⌘X</Menu.ItemCommand>
</Menu.Item>
<Menu.Item value="copy" valueText="copy">
<LuCopy />
<Box flex="1">Copy</Box>
<Menu.ItemCommand>⌘C</Menu.ItemCommand>
</Menu.Item>
<Menu.Item value="paste" valueText="paste">
<LuClipboardPaste />
<Box flex="1">Paste</Box>
<Menu.ItemCommand>⌘V</Menu.ItemCommand>
</Menu.Item>
</Menu.Content>
</Menu.Root>
)
}
Placement
Use the positioning.placement
prop to control the placement of the menu.
'use client'
import { Button, Menu } from '@saas-ui/react'
export const MenuWithPlacement = () => {
return (
<Menu.Root positioning={{ placement: 'right-start' }}>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<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.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.
'use client'
import { Box, Group } from '@chakra-ui/react'
import { Button, Menu } from '@saas-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 /> },
]
export const MenuWithMixedLayout = () => {
return (
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="outline" size="sm">
Open
</Button>
</Menu.Trigger>
<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.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. |
skipAnimationOnMount | false | boolean Whether to allow the initial presence animation. |
typeahead | true | boolean Whether the pressing printable characters should trigger typeahead navigation |
unmountOnExit | false | boolean Whether to unmount on exit. |
colorPalette | 'gray' | 'gray' | 'zinc' | 'neutral' | 'stone' | 'red' | 'orange' | 'amber' | 'yellow' | 'lime' | 'green' | 'emerald' | 'teal' | 'cyan' | 'sky' | 'blue' | 'indigo' | 'violet' | 'purple' | 'fuchsia' | 'pink' | 'rose' | 'presence' | 'status' | 'sidebar' | 'sidebar.accent' | 'accent' | 'slate' 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 | |
defaultHighlightedValue | string The 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 | boolean The initial open state of the menu when rendered. Use when you don't need to control the open state of the menu. | |
highlightedValue | string The controlled highlighted value of the 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 | VoidFunction 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 The controlled open state of the menu | |
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 | |
onSelect | VoidFunction The function to call when the item is selected | |
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. |