# Menu URL: https://ark-ui.com/docs/components/menu Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/components/menu.mdx A list of options that appears when a user interacts with a button. --- ## Anatomy ```tsx ``` ## Examples **Example: basic** ```ripple import { Menu } from 'ark-ripple/menu'; import { ChevronDown } from 'lucide-ripple'; import styles from 'styles/menu.module.css'; export component Basic() { {'File'} {'New File'} {'Open...'} {'Save'} {'Save As...'} } ``` ### Item Selection Use `onSelect` to handle item selection. The callback receives the item's `id`. **Example: controlled** ```ripple import { Menu } from 'ark-ripple/menu'; import { ChevronDown } from 'lucide-ripple'; import { track } from 'ripple'; import button from 'styles/button.module.css'; import styles from 'styles/menu.module.css'; export component Controlled() { let open = track(false);
{ @open = e.open; }} > {'Actions'} {'Edit'} {'Duplicate'} {'Archive'} {'Delete'}
} ``` ### Root Provider An alternative way to control the menu is to use the `RootProvider` component and the `useMenu` hook. This way you can access the state and methods from outside the component. **Example: root-provider** ```ripple import { Menu, useMenu } from 'ark-ripple/menu'; import { ChevronDown } from 'lucide-ripple'; import button from 'styles/button.module.css'; import styles from 'styles/menu.module.css'; export component RootProvider() { const menu = useMenu();
{'Edit'} {'Cut'} {'Copy'} {'Paste'} {'Delete'}
} ``` ### Grouping Use `Menu.ItemGroup` and `Menu.ItemGroupLabel` to organize related menu items. **Example: group** ```ripple import { Menu } from 'ark-ripple/menu'; import { ChevronDown } from 'lucide-ripple'; import styles from 'styles/menu.module.css'; export component Group() { {'Edit'} {'Clipboard'} {'Cut'} {'Copy'} {'Paste'} {'Selection'} {'Select All'} {'Deselect'} } ``` ### Links To render menu items as links, use the `asChild` prop to replace the default element with an anchor tag. **Example: links** ```ripple import { Menu } from 'ark-ripple/menu'; import { ChevronDown } from 'lucide-ripple'; import styles from 'styles/menu.module.css'; export component Links() { {'Help'} component asChild({ propsFn }) { {'Documentation'} } component asChild({ propsFn }) { {'GitHub'} } component asChild({ propsFn }) { {'Changelog'} } } ``` ### Checkbox To add a checkbox to a menu item, use the `Menu.Checkbox` component. **Example: checkbox-items** ```ripple import { Menu } from 'ark-ripple/menu'; import { Check, ChevronDown } from 'lucide-ripple'; import { track } from 'ripple'; import styles from 'styles/menu.module.css'; export component CheckboxItems() { let showToolbar = track(true); let showStatusBar = track(false); {'View'} { @showToolbar = v; }} value="toolbar" > {'Show Toolbar'} { @showStatusBar = v; }} value="status-bar" > {'Show Status Bar'} } ``` ### Radio Group To group radio option items, use the `Menu.RadioGroup` component. **Example: radio-items** ```ripple import { Menu } from 'ark-ripple/menu'; import { Check, ChevronDown } from 'lucide-ripple'; import { track } from 'ripple'; import styles from 'styles/menu.module.css'; export component RadioItems() { let sortBy = track('date'); {'Sort'} { @sortBy = e.value; }} > {'Sort By'} {'Name'} {'Date Modified'} {'Size'} {'Type'} } ``` ### Context Menu To show the menu when a trigger element is right-clicked, use the `Menu.ContextTrigger` component. Context menus are also opened during a long-press of roughly `700ms` when the pointer is pen or touch. **Example: context** ```ripple import { Menu } from 'ark-ripple/menu'; import styles from 'styles/menu.module.css'; export component Context() { {'Right click here'} {'Cut'} {'Copy'} {'Paste'} {'Delete'} } ``` ### Nested To show a nested menu, render another `Menu` component and use the `Menu.TriggerItem` component to open the submenu. **Example: nested** ```ripple import { Menu } from 'ark-ripple/menu'; import { ChevronDown } from 'lucide-ripple'; import styles from 'styles/menu.module.css'; export component Nested() { {'File'} {'New File'} {'Open...'} {'Share'} {'Email'} {'Message'} {'AirDrop'} {'Export'} {'PDF'} {'PNG'} {'SVG'} {'Print...'} } ``` ### Menu in Dialog When rendering a menu inside a dialog, use `lazyMount` and `unmountOnExit` to ensure proper cleanup when the dialog closes. **Example: menu-in-dialog** ```ripple import { Dialog } from 'ark-ripple/dialog'; import { Menu } from 'ark-ripple/menu'; import { Portal } from 'ark-ripple/portal'; import { ChevronDown, X } from 'lucide-ripple'; import button from 'styles/button.module.css'; import dialog from 'styles/dialog.module.css'; import styles from 'styles/menu.module.css'; export component MenuInDialog() { {'Open Dialog'} {'Settings'} {'Configure your preferences below.'}
{'Select theme'} {'Light'} {'Dark'} {'System'}
} ``` ### Menu Item Dialog Open a confirmation dialog from a menu item. This pattern is useful for destructive actions like delete that require user confirmation. **Example: menu-item-dialog** ```ripple import { Dialog } from 'ark-ripple/dialog'; import { Menu } from 'ark-ripple/menu'; import { Portal } from 'ark-ripple/portal'; import { ChevronDown, X } from 'lucide-ripple'; import { track } from 'ripple'; import button from 'styles/button.module.css'; import dialog from 'styles/dialog.module.css'; import styles from 'styles/menu.module.css'; export component MenuItemDialog() { let dialogOpen = track(false); {'Actions'} {'Edit'} {'Duplicate'} { @dialogOpen = true; }} > {'Delete...'} { @dialogOpen = e.open; }} role="alertdialog" > {'Confirm Delete'} {'Are you sure you want to delete this item? This action cannot be undone.'}
} ``` ## Guides ### Custom IDs Ark UI autogenerates ids for menu items internally. Passing a custom `id` prop breaks the internal `getElementById` functionality used by the component. ```tsx // ❌ Don't do this Custom Item // ✅ Do this Custom Item ``` ### Links To render a menu item as a link, render the link as the menu item itself using `asChild`, not as a child of the menu item. > This pattern ensures the link element receives the correct ARIA attributes and keyboard interactions from the menu > item. Here's an example of a reusable `MenuItemLink` component: ```tsx interface MenuItemLinkProps extends Menu.ItemProps { href?: string target?: string } export component MenuItemLink({ href, target, children, ...rest }: MenuItemLinkProps) { return ( component asChild({ propsFn }) { <@children /> } ) } ``` ## API Reference ### Props **Component API Reference** **Root Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `anchorPoint` | `Point` | No | The positioning point for the menu. Can be set by the context menu trigger or the button trigger. | | `aria-label` | `string` | No | The accessibility label for the menu | | `closeOnSelect` | `boolean` | No | Whether to close the menu when an option is selected | | `composite` | `boolean` | No | Whether the menu is a composed with other composite widgets like a combobox or tabs | | `defaultHighlightedValue` | `string` | No | 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` | No | 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` | No | The controlled highlighted value of the menu item. | | `id` | `string` | No | 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 }>` | No | The ids of the elements in the menu. Useful for composition. | | `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame | | `lazyMount` | `boolean` | No | Whether to enable lazy mounting | | `loopFocus` | `boolean` | No | Whether to loop the keyboard navigation. | | `navigate` | `(details: NavigateDetails) => void` | No | Function to navigate to the selected item if it's an anchor element | | `onEscapeKeyDown` | `(event: KeyboardEvent) => void` | No | Function called when the escape key is pressed | | `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state | | `onFocusOutside` | `(event: FocusOutsideEvent) => void` | No | Function called when the focus is moved outside the component | | `onHighlightChange` | `(details: HighlightChangeDetails) => void` | No | Function called when the highlighted menu item changes. | | `onInteractOutside` | `(event: InteractOutsideEvent) => void` | No | Function called when an interaction happens outside the component | | `onOpenChange` | `(details: OpenChangeDetails) => void` | No | Function called when the menu opens or closes | | `onPointerDownOutside` | `(event: PointerDownOutsideEvent) => void` | No | Function called when the pointer is pressed down outside the component | | `onRequestDismiss` | `(event: LayerDismissEvent) => void` | No | Function called when this layer is closed due to a parent layer being closed | | `onSelect` | `(details: SelectionDetails) => void` | No | Function called when a menu item is selected. | | `open` | `boolean` | No | The controlled open state of the menu | | `positioning` | `PositioningOptions` | No | The options used to dynamically position the menu | | `present` | `boolean` | No | Whether the node is present (controlled by the user) | | `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. | | `typeahead` | `boolean` | No | Whether the pressing printable characters should trigger typeahead navigation | | `unmountOnExit` | `boolean` | No | Whether to unmount on exit. | **Arrow Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **Arrow CSS Variables:** | Variable | Description | |----------|-------------| | `--arrow-size` | The size of the arrow | | `--arrow-size-half` | Half the size of the arrow | | `--arrow-background` | Use this variable to style the arrow background | | `--arrow-offset` | The offset position of the arrow | **ArrowTip Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **CheckboxItem Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `checked` | `boolean` | Yes | Whether the option is checked | | `value` | `string` | Yes | The value of the option | | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | | `closeOnSelect` | `boolean` | No | Whether the menu should be closed when the option is selected. | | `disabled` | `boolean` | No | Whether the menu item is disabled | | `onCheckedChange` | `(checked: boolean) => void` | No | Function called when the option state is changed | | `valueText` | `string` | No | 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. | **Content Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **Content Data Attributes:** | Attribute | Value | |-----------|-------| | `[data-scope]` | menu | | `[data-part]` | content | | `[data-state]` | "open" | "closed" | | `[data-nested]` | menu | | `[data-has-nested]` | menu | | `[data-placement]` | The placement of the content | **Content CSS Variables:** | Variable | Description | |----------|-------------| | `--layer-index` | The index of the dismissable in the layer stack | | `--nested-layer-count` | The number of nested menus | **ContextTrigger Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **ContextTrigger Data Attributes:** | Attribute | Value | |-----------|-------| | `[data-scope]` | menu | | `[data-part]` | context-trigger | | `[data-state]` | "open" | "closed" | **Indicator Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **Indicator Data Attributes:** | Attribute | Value | |-----------|-------| | `[data-scope]` | menu | | `[data-part]` | indicator | | `[data-state]` | "open" | "closed" | **ItemGroupLabel Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **ItemGroup Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **ItemIndicator Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **ItemIndicator Data Attributes:** | Attribute | Value | |-----------|-------| | `[data-scope]` | menu | | `[data-part]` | item-indicator | | `[data-disabled]` | Present when disabled | | `[data-highlighted]` | Present when highlighted | | `[data-state]` | "checked" | **Item Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `value` | `string` | Yes | The unique value of the menu item option. | | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | | `closeOnSelect` | `boolean` | No | Whether the menu should be closed when the option is selected. | | `disabled` | `boolean` | No | Whether the menu item is disabled | | `onSelect` | `VoidFunction` | No | The function to call when the item is selected | | `valueText` | `string` | No | 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. | **Item Data Attributes:** | Attribute | Value | |-----------|-------| | `[data-scope]` | menu | | `[data-part]` | item | | `[data-disabled]` | Present when disabled | | `[data-highlighted]` | Present when highlighted | | `[data-value]` | The value of the item | | `[data-valuetext]` | The human-readable value | **ItemText Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **ItemText Data Attributes:** | Attribute | Value | |-----------|-------| | `[data-scope]` | menu | | `[data-part]` | item-text | | `[data-disabled]` | Present when disabled | | `[data-highlighted]` | Present when highlighted | | `[data-state]` | "checked" | **Positioner Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **Positioner CSS Variables:** | Variable | Description | |----------|-------------| | `--reference-width` | The width of the reference element | | `--reference-height` | The height of the root | | `--available-width` | The available width in viewport | | `--available-height` | The available height in viewport | | `--x` | The x position for transform | | `--y` | The y position for transform | | `--z-index` | The z-index value | | `--transform-origin` | The transform origin for animations | **RadioItemGroup Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | | `onValueChange` | `(e: ValueChangeDetails) => void` | No | | | `value` | `string` | No | | **RadioItem Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `value` | `string` | Yes | The value of the option | | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | | `closeOnSelect` | `boolean` | No | Whether the menu should be closed when the option is selected. | | `disabled` | `boolean` | No | Whether the menu item is disabled | | `valueText` | `string` | No | 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. | **RootProvider Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `value` | `UseMenuReturn` | Yes | | | `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame | | `lazyMount` | `boolean` | No | Whether to enable lazy mounting | | `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state | | `present` | `boolean` | No | Whether the node is present (controlled by the user) | | `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. | | `unmountOnExit` | `boolean` | No | Whether to unmount on exit. | **Separator Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **TriggerItem Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **Trigger Props:** | Prop | Type | Required | Description | |------|------|----------|-------------| | `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. | **Trigger Data Attributes:** | Attribute | Value | |-----------|-------| | `[data-scope]` | menu | | `[data-part]` | trigger | | `[data-placement]` | The placement of the trigger | | `[data-controls]` | | | `[data-state]` | "open" | "closed" | ### Context **API:** | Property | Type | Description | |----------|------|-------------| | `open` | `boolean` | Whether the menu is open | | `setOpen` | `(open: boolean) => void` | Function to open or close the menu | | `highlightedValue` | `string` | The id of the currently highlighted menuitem | | `setHighlightedValue` | `(value: string) => void` | Function to set the highlighted menuitem | | `setParent` | `(parent: ParentMenuService) => void` | Function to register a parent menu. This is used for submenus | | `setChild` | `(child: ChildMenuService) => void` | Function to register a child menu. This is used for submenus | | `reposition` | `(options?: Partial) => void` | Function to reposition the popover | | `getOptionItemState` | `(props: OptionItemProps) => OptionItemState` | Returns the state of the option item | | `getItemState` | `(props: ItemProps) => ItemState` | Returns the state of the menu item | | `addItemListener` | `(props: ItemListenerProps) => VoidFunction` | Setup the custom event listener for item selection event | ## Accessibility Complies with the [Menu WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menubar/). ### Keyboard Support