Nested Menu

A list of options that appears when a user interacts with a button.

Anatomy

To set up the menu correctly, you’ll need to understand its anatomy and how we name its parts.

Each part includes a data-part attribute to help identify them in the DOM.

Examples

To show a nested menu, render another Menu component and use the Menu.TriggerItem component to open the submenu.

import { Menu, Portal } from '@ark-ui/react'

const Basic = () => (
  <Menu.Root>
    <Menu.Trigger>Open menu</Menu.Trigger>
    <Menu.Positioner>
      <Menu.Content>
        <Menu.Item id="new-tab">New Tab...</Menu.Item>
        <Menu.Item id="new-win">New Window...</Menu.Item>
        <Menu.Separator />
        <Menu.Root>
          <Menu.TriggerItem>Share &gt;</Menu.TriggerItem>
          <Portal>
            <Menu.Positioner>
              <Menu.Content>
                <Menu.Item id="twitter">Twitter</Menu.Item>
                <Menu.Item id="message">Message</Menu.Item>
              </Menu.Content>
            </Menu.Positioner>
          </Portal>
        </Menu.Root>
      </Menu.Content>
    </Menu.Positioner>
  </Menu.Root>
)

Checkbox and Radio option items

To show a checkbox or radio option item, use the Menu.OptionItem component. Depending on the type prop, the item will be rendered as a checkbox or radio option item. The name prop is used to group the items together, and the value prop is used to identify the item.

To manage the state of the option items pass the value and onValueChange props to the Menu component.

import { Menu, Portal } from '@ark-ui/react'
import { useState } from 'react'

const Advanced = () => {
  const [value, setValue] = useState<Record<string, string | string[]>>({
    framework: '',
    libraries: [],
  })
  return (
    <Menu.Root
      value={value}
      onValueChange={(data) => {
        setValue((prev) => ({
          ...prev,
          [data.name]: data.value,
        }))
      }}
    >
      <Menu.Trigger>Open menu</Menu.Trigger>
      <Menu.Positioner>
        <Menu.Content>
          <Menu.ItemGroup id="radio-group">
            <Menu.ItemGroupLabel htmlFor="radio-group">Radio Group</Menu.ItemGroupLabel>
            <Menu.OptionItem name="framework" type="radio" value="react">
              {({ isChecked }) => <>{isChecked ? '✅' : ''} React</>}
            </Menu.OptionItem>
            <Menu.OptionItem name="framework" type="radio" value="solid">
              {({ isChecked }) => <>{isChecked ? '✅' : ''} Solid</>}
            </Menu.OptionItem>
            <Menu.OptionItem name="framework" type="radio" value="vue">
              {({ isChecked }) => <>{isChecked ? '✅' : ''} Vue</>}
            </Menu.OptionItem>
          </Menu.ItemGroup>
          <Menu.Root>
            <Menu.TriggerItem>Share &gt;</Menu.TriggerItem>
            <Portal>
              <Menu.Positioner>
                <Menu.Content>
                  <Menu.Item id="twitter">Twitter</Menu.Item>
                  <Menu.Item id="message">Message</Menu.Item>
                </Menu.Content>
              </Menu.Positioner>
            </Portal>
          </Menu.Root>
        </Menu.Content>
      </Menu.Positioner>
    </Menu.Root>
  )
}

API Reference

Item

PropTypeDefault
id
string
asChild
boolean
closeOnSelect
boolean
disabled
boolean
valueText
string

Root

PropTypeDefault
anchorPoint
Point
aria-label
string
closeOnSelect
boolean
dir
'ltr' | 'rtl'"ltr"
getRootNode
() => Node | ShadowRoot | Document
highlightedId
string
id
string
ids
Partial<{ trigger: string contextTrigger: string content: string label(id: string): string group(id: string): string positioner: string arrow: string }>
lazyMount
booleanfalse
loop
boolean
onExitComplete
() => void
onFocusOutside
(event: FocusOutsideEvent) => void
onInteractOutside
(event: InteractOutsideEvent) => void
onOpenChange
(details: OpenChangeDetails) => void
onPointerDownOutside
(event: PointerDownOutsideEvent) => void
onSelect
(details: SelectionDetails) => void
onValueChange
(details: ValueChangeDetails) => void
open
boolean
positioning
PositioningOptions
present
boolean
unmountOnExit
booleanfalse
value
Record<string, string | string[]>

Arrow

PropTypeDefault
asChild
boolean

Content

PropTypeDefault
asChild
boolean

Trigger

PropTypeDefault
asChild
boolean

ArrowTip

PropTypeDefault
asChild
boolean

ItemGroup

PropTypeDefault
id
string
asChild
boolean

Separator

PropTypeDefault
asChild
boolean

OptionItem

PropTypeDefault
name
string
type
'checkbox' | 'radio'
value
string
asChild
boolean
closeOnSelect
boolean
disabled
boolean
onCheckedChange
(checked: boolean) => void
valueText
string

Positioner

PropTypeDefault
asChild
boolean

TriggerItem

PropTypeDefault
asChild
boolean

ContextTrigger

PropTypeDefault
asChild
boolean

ItemGroupLabel

PropTypeDefault
htmlFor
string
asChild
boolean