Nested Menu
A list of options that appears when a user interacts with a button.
A list of options that appears when a user interacts with a button.
To set up the menu correctly, you’ll need to understand its anatomy and how we name its parts.
Each part includes a
data-partattribute to help identify them in the DOM.
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 ></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>
)
import { Menu } from '@ark-ui/solid'
import { Portal } from 'solid-js/web'
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 ></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>
)
<script setup lang="ts">
import { ref } from 'vue'
import { Menu } from '@ark-ui/vue'
const value = ref({
  framework: '',
  libraries: [],
})
</script>
<template>
  <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 ></Menu.TriggerItem>
          <Menu.Positioner>
            <Menu.Content>
              <Menu.Item id="twitter">Twitter</Menu.Item>
              <Menu.Item id="message">Message</Menu.Item>
            </Menu.Content>
          </Menu.Positioner>
        </Menu.Root>
      </Menu.Content>
    </Menu.Positioner>
  </Menu.Root>
</template>
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 ></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>
  )
}
import { Menu } from '@ark-ui/solid'
import { createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
const Advanced = () => {
  const [value, setValue] = createSignal<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 for="radio-group">Radio Group</Menu.ItemGroupLabel>
            <Menu.OptionItem name="framework" type="radio" value="react">
              {(itemState) => <>{itemState().isChecked ? '✅' : ''} React</>}
            </Menu.OptionItem>
            <Menu.OptionItem name="framework" type="radio" value="solid">
              {(itemState) => <>{itemState().isChecked ? '✅' : ''} Solid</>}
            </Menu.OptionItem>
            <Menu.OptionItem name="framework" type="radio" value="vue">
              {(itemState) => <>{itemState().isChecked ? '✅' : ''} Vue</>}
            </Menu.OptionItem>
          </Menu.ItemGroup>
          <Menu.Root>
            <Portal>
              <Menu.TriggerItem>Share ></Menu.TriggerItem>
              <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>
  )
}
<script setup lang="ts">
import { ref } from 'vue'
import { Menu } from '@ark-ui/vue'
const value = ref({
  framework: '',
  libraries: [],
})
</script>
<template>
  <Menu.Root v-model="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" v-slot="{ isChecked }">
            {{ isChecked ? '✅' : '' }} React
          </Menu.OptionItem>
          <Menu.OptionItem name="framework" type="radio" value="solid" v-slot="{ isChecked }">
            {{ isChecked ? '✅' : '' }} Solid
          </Menu.OptionItem>
          <Menu.OptionItem name="framework" type="radio" value="vue" v-slot="{ isChecked }">
            {{ isChecked ? '✅' : '' }} Solid
          </Menu.OptionItem>
        </Menu.ItemGroup>
        <Menu.Root>
          <Menu.TriggerItem>Share ></Menu.TriggerItem>
          <Menu.Positioner>
            <Menu.Content>
              <Menu.Item id="twitter">Twitter</Menu.Item>
              <Menu.Item id="message">Message</Menu.Item>
            </Menu.Content>
          </Menu.Positioner>
        </Menu.Root>
      </Menu.Content>
    </Menu.Positioner>
  </Menu.Root>
</template>
| Prop | Type | Default | 
|---|---|---|
| idThe `id` of the menu item option. | string | |
| asChildRender as a different element type. | boolean | |
| closeOnSelectWhether the menu should be closed when the option is selected. | boolean | |
| disabledWhether the menu item is disabled | boolean | |
| valueTextThe 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. | string | 
| Prop | Type | Default | 
|---|---|---|
| anchorPointThe positioning point for the menu. Can be set by the context menu trigger or the button trigger. | Point | |
| aria-labelThe accessibility label for the menu | string | |
| closeOnSelectWhether to close the menu when an option is selected | boolean | |
| dirThe document's text/writing direction. | 'ltr' | 'rtl' | "ltr" | 
| getRootNodeA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron. | () => Node | ShadowRoot | Document | |
| highlightedIdThe `id` of the active menu item. | string | |
| idThe unique identifier of the machine. | string | |
| idsThe ids of the elements in the menu. Useful for composition. | Partial<{
  trigger: string
  contextTrigger: string
  content: string
  label(id: string): string
  group(id: string): string
  positioner: string
  arrow: string
}> | |
| lazyMountWhether to enable lazy mounting | boolean | false | 
| loopWhether to loop the keyboard navigation. | boolean | |
| onExitCompleteFunction called when the animation ends in the closed state. | () => void | |
| onFocusOutsideFunction called when the focus is moved outside the component | (event: FocusOutsideEvent) => void | |
| onInteractOutsideFunction called when an interaction happens outside the component | (event: InteractOutsideEvent) => void | |
| onOpenChangeFunction called when the menu opens or closes | (details: OpenChangeDetails) => void | |
| onPointerDownOutsideFunction called when the pointer is pressed down outside the component | (event: PointerDownOutsideEvent) => void | |
| onSelectFunction called when a menu item is selected. | (details: SelectionDetails) => void | |
| onValueChangeCallback to be called when the menu values change (for radios and checkboxes). | (details: ValueChangeDetails) => void | |
| openWhether the menu is open | boolean | |
| positioningThe options used to dynamically position the menu | PositioningOptions | |
| presentWhether the node is present (controlled by the user) | boolean | |
| unmountOnExitWhether to unmount on exit. | boolean | false | 
| valueThe values of radios and checkboxes in the menu. | Record<string, string | string[]> | 
| Prop | Type | Default | 
|---|---|---|
| asChildRender as a different element type. | boolean | 
| Prop | Type | Default | 
|---|---|---|
| asChildRender as a different element type. | boolean | 
| Prop | Type | Default | 
|---|---|---|
| asChildRender as a different element type. | boolean | 
| Prop | Type | Default | 
|---|---|---|
| asChildRender as a different element type. | boolean | 
| Prop | Type | Default | 
|---|---|---|
| idThe `id` of the element that provides accessibility label to the option group | string | |
| asChildRender as a different element type. | boolean | 
| Prop | Type | Default | 
|---|---|---|
| asChildRender as a different element type. | boolean | 
| Prop | Type | Default | 
|---|---|---|
| nameThe option's name as specified in menu's `context.values` object | string | |
| typeWhether the option is a radio or a checkbox | 'checkbox' | 'radio' | |
| valueThe value of the option | string | |
| asChildRender as a different element type. | boolean | |
| closeOnSelectWhether the menu should be closed when the option is selected. | boolean | |
| disabledWhether the menu item is disabled | boolean | |
| onCheckedChangeFunction called when the option state is changed | (checked: boolean) => void | |
| valueTextThe 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. | string | 
| Prop | Type | Default | 
|---|---|---|
| asChildRender as a different element type. | boolean | 
| Prop | Type | Default | 
|---|---|---|
| asChildRender as a different element type. | boolean | 
| Prop | Type | Default | 
|---|---|---|
| asChildRender as a different element type. | boolean | 
| Prop | Type | Default | 
|---|---|---|
| htmlFor | string | |
| asChildRender as a different element type. | boolean | 
Previous
MenuNext
Number Input