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-part
attribute to help identify them in the DOM.
Learn how to use the Menu
component in your project. Let’s take a look at the
most basic example:
import { Menu } from '@ark-ui/react'
const Basic = () => (
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item id="search">Search</Menu.Item>
<Menu.Item id="undo">Undo</Menu.Item>
<Menu.Item id="delivery" disabled>
Delivery
</Menu.Item>
<Menu.Item id="unlink">Unlink</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
import { Menu } from '@ark-ui/solid'
const Basic = () => (
<Menu.Root>
<Menu.Trigger>Open Menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item id="search">Search</Menu.Item>
<Menu.Item id="undo">Undo</Menu.Item>
<Menu.Item id="delivery" disabled>
Delivery
</Menu.Item>
<Menu.Item id="unlink">Unlink</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
<script setup lang="ts">
import { ref } from 'vue'
import { Menu } from '@ark-ui/vue'
const isOpen = ref(false)
const value = ref({
framework: '',
libraries: [],
})
</script>
<template>
<Menu.Root lazyMount closeOnSelect>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item id="search">Search</Menu.Item>
<Menu.Item id="undo">Undo</Menu.Item>
<Menu.Item id="delivery" disabled> Delivery </Menu.Item>
<Menu.Item id="unlink">Unlink</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</template>
Pass the onSelect
prop to the Menu component to perform some custom logic when
an item is selected. The callback is invoked with the id
of the item.
import { Menu } from '@ark-ui/react'
import { useState } from 'react'
const Controlled = () => {
const [isOpen, setIsOpen] = useState(false)
return (
<>
<button onClick={() => setIsOpen(!isOpen)}>Trigger from the outside</button>
<Menu.Root open={isOpen} onSelect={(id) => console.log(id)}>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item id="search">Search</Menu.Item>
<Menu.Item id="undo">Undo</Menu.Item>
<Menu.Item id="delivery" disabled>
Delivery
</Menu.Item>
<Menu.Item id="unlink">Unlink</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</>
)
}
import { Menu } from '@ark-ui/solid'
import { createSignal } from 'solid-js'
const Controlled = () => {
const [isOpen, setIsOpen] = createSignal(false)
return (
<>
<button onClick={() => setIsOpen(!isOpen())}>Trigger from the outside</button>
<Menu.Root open={isOpen()} onSelect={(id) => console.log(id)}>
<Menu.Trigger>Toggle Menu</Menu.Trigger>{' '}
<Menu.Positioner>
<Menu.Content>
<Menu.Item id="search">Search</Menu.Item>
<Menu.Item id="undo">Undo</Menu.Item>
<Menu.Item id="delivery" disabled>
Delivery
</Menu.Item>
<Menu.Item id="unlink">Unlink</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</>
)
}
<script setup lang="ts">
import { ref } from 'vue'
import { Menu } from '@ark-ui/vue'
const isOpen = ref(false)
const value = ref({
framework: '',
libraries: [],
})
</script>
<template>
<button @click="isOpen = !isOpen">Trigger from the outside</button>
<Menu.Root :open="isOpen" @select="(id) => console.log(id)">
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item id="search">Search</Menu.Item>
<Menu.Item id="undo">Undo</Menu.Item>
<Menu.Item id="delivery" disabled> Delivery </Menu.Item>
<Menu.Item id="unlink">Unlink</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</template>
When the number of menu items gets much, it might be useful to group related
menu items. To achieve this, render the Menu.ItemGroup
component around the
Menu.Item
components. The Menu.ItemGroupLabel
component can be used to add a
label to the group.
import { Menu } from '@ark-ui/react'
const Group = () => (
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.ItemGroup id="group-1">
<Menu.ItemGroupLabel htmlFor="group-1">Group 1</Menu.ItemGroupLabel>
<Menu.Item id="share">Share...</Menu.Item>
<Menu.Item id="move">Move...</Menu.Item>
</Menu.ItemGroup>
<Menu.ItemGroup id="group-2">
<Menu.ItemGroupLabel htmlFor="group-2">Group 2</Menu.ItemGroupLabel>
<Menu.Item id="rename">Rename...</Menu.Item>
<Menu.Item id="delete">Delete...</Menu.Item>
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
import { Menu } from '@ark-ui/solid'
const Group = () => (
<Menu.Root>
<Menu.Trigger>Open Menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.ItemGroup id="group-1">
<Menu.ItemGroupLabel for="group-1">Group 1</Menu.ItemGroupLabel>
<Menu.Item id="share">Share...</Menu.Item>
<Menu.Item id="move">Move...</Menu.Item>
</Menu.ItemGroup>
<Menu.ItemGroup id="group-2">
<Menu.ItemGroupLabel for="group-2">Group 2</Menu.ItemGroupLabel>
<Menu.Item id="rename">Rename...</Menu.Item>
<Menu.Item id="delete">Delete...</Menu.Item>
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
<script setup lang="ts">
import { ref } from 'vue'
import { Menu } from '@ark-ui/vue'
const isOpen = ref(false)
const value = ref({
framework: '',
libraries: [],
})
</script>
<template>
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.ItemGroup id="group-1">
<Menu.ItemGroupLabel htmlFor="group-1">Group 1</Menu.ItemGroupLabel>
<Menu.Item id="share">Share...</Menu.Item>
<Menu.Item id="move">Move...</Menu.Item>
</Menu.ItemGroup>
<Menu.ItemGroup id="group-2">
<Menu.ItemGroupLabel htmlFor="group-2">Group 2</Menu.ItemGroupLabel>
<Menu.Item id="rename">Rename...</Menu.Item>
<Menu.Item id="delete">Delete...</Menu.Item>
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</template>
To separate menu items, render the Menu.Separator
component.
import { Menu } from '@ark-ui/react'
const Separator = () => (
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item id="search">Search</Menu.Item>
<Menu.Item id="undo">Undo</Menu.Item>
<Menu.Separator />
<Menu.Item id="delivery" disabled>
Delivery
</Menu.Item>
<Menu.Item id="unlink">Unlink</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
import { Menu } from '@ark-ui/solid'
const Separator = () => (
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item id="search">Search</Menu.Item>
<Menu.Item id="undo">Undo</Menu.Item>
<Menu.Separator />
<Menu.Item id="delivery" disabled>
Delivery
</Menu.Item>
<Menu.Item id="unlink">Unlink</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
<script setup lang="ts">
import { ref } from 'vue'
import { Menu } from '@ark-ui/vue'
const isOpen = ref(false)
const value = ref({
framework: '',
libraries: [],
})
</script>
<template>
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item id="search">Search</Menu.Item>
<Menu.Item id="undo">Undo</Menu.Item>
<Menu.Separator />
<Menu.Item id="delivery" disabled> Delivery </Menu.Item>
<Menu.Item id="unlink">Unlink</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</template>
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.
import { Menu } from '@ark-ui/react'
const ContextMenu = () => (
<Menu.Root>
<Menu.ContextTrigger>
<div style={{ width: '100%', height: '20rem', border: '1px solid lightgray' }}>
Some content
</div>
</Menu.ContextTrigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item id="search">Search</Menu.Item>
<Menu.Item id="undo">Undo</Menu.Item>
<Menu.Item id="delivery" disabled>
Delivery
</Menu.Item>
<Menu.Item id="unlink">Unlink</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
import { Menu } from '@ark-ui/solid'
const ContextMenu = () => (
<Menu.Root>
<Menu.ContextTrigger>
<div style={{ width: '100%', height: '20rem', border: '1px solid lightgray' }}>
Some content
</div>
</Menu.ContextTrigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item id="search">Search</Menu.Item>
<Menu.Item id="undo">Undo</Menu.Item>
<Menu.Item id="delivery" disabled>
Delivery
</Menu.Item>
<Menu.Item id="unlink">Unlink</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
<script setup lang="ts">
import { ref } from 'vue'
import { Menu } from '@ark-ui/vue'
const isOpen = ref(false)
const value = ref({
framework: '',
libraries: [],
})
</script>
<template>
<Menu.Root>
<Menu.ContextTrigger>
<div :style="{ width: '100%', height: '20rem', border: '1px solid lightgray' }">
Some content
</div>
</Menu.ContextTrigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item id="search">Search</Menu.Item>
<Menu.Item id="undo">Undo</Menu.Item>
<Menu.Item id="delivery" disabled> Delivery </Menu.Item>
<Menu.Item id="unlink">Unlink</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</template>
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 SubMenu = () => (
<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>
<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>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
import { Menu } from '@ark-ui/solid'
import { Portal } from 'solid-js/web'
const SubMenu = () => (
<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>
<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>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
<script setup lang="ts">
import { ref } from 'vue'
import { Menu } from '@ark-ui/vue'
const isOpen = ref(false)
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 } from '@ark-ui/react'
import { useState } from 'react'
const Options = () => {
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.ItemGroup id="checkbox-group">
<Menu.ItemGroupLabel htmlFor="checkbox-group">Checkbox Group</Menu.ItemGroupLabel>
<Menu.OptionItem name="libraries" type="checkbox" value="zag-js">
{({ isChecked }) => <>{isChecked ? '✅' : ''} zag-js</>}
</Menu.OptionItem>
<Menu.OptionItem name="libraries" type="checkbox" value="ark">
{({ isChecked }) => <>{isChecked ? '✅' : ''} ark</>}
</Menu.OptionItem>
<Menu.OptionItem name="libraries" type="checkbox" value="panda">
{({ isChecked }) => <>{isChecked ? '✅' : ''} panda</>}
</Menu.OptionItem>
<Menu.OptionItem name="libraries" type="checkbox" value="chakra">
{({ isChecked }) => <>{isChecked ? '✅' : ''} chakra</>}
</Menu.OptionItem>
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
}
import { Menu } from '@ark-ui/solid'
import { createSignal } from 'solid-js'
const Options = () => {
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.ItemGroup id="checkbox-group">
<Menu.ItemGroupLabel for="checkbox-group">Checkbox Group</Menu.ItemGroupLabel>
<Menu.OptionItem name="libraries" type="checkbox" value="zag-js">
{(itemState) => <>{itemState().isChecked ? '✅' : ''} zag-js</>}
</Menu.OptionItem>
<Menu.OptionItem name="libraries" type="checkbox" value="ark">
{(itemState) => <>{itemState().isChecked ? '✅' : ''} ark</>}
</Menu.OptionItem>
<Menu.OptionItem name="libraries" type="checkbox" value="panda">
{(itemState) => <>{itemState().isChecked ? '✅' : ''} panda</>}
</Menu.OptionItem>
<Menu.OptionItem name="libraries" type="checkbox" value="chakra">
{(itemState) => <>{itemState().isChecked ? '✅' : ''} chakra</>}
</Menu.OptionItem>
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
}
<script setup lang="ts">
import { ref } from 'vue'
import { Menu } from '@ark-ui/vue'
const isOpen = ref(false)
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 ? '✅' : '' }} Vue
</Menu.OptionItem>
</Menu.ItemGroup>
<Menu.ItemGroup id="checkbox-group">
<Menu.ItemGroupLabel htmlFor="checkbox-group">Checkbox Group</Menu.ItemGroupLabel>
<Menu.OptionItem name="libraries" type="checkbox" value="zag-js" v-slot="{ isChecked }">
{{ isChecked ? '✅' : '' }} zag-js
</Menu.OptionItem>
<Menu.OptionItem name="libraries" type="checkbox" value="ark" v-slot="{ isChecked }">
{{ isChecked ? '✅' : '' }} ark
</Menu.OptionItem>
<Menu.OptionItem name="libraries" type="checkbox" value="panda" v-slot="{ isChecked }">
{{ isChecked ? '✅' : '' }} panda
</Menu.OptionItem>
<Menu.OptionItem name="libraries" type="checkbox" value="chakra" v-slot="{ isChecked }">
{{ isChecked ? '✅' : '' }} chakra
</Menu.OptionItem>
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</template>
Prop | Type | Default |
---|---|---|
anchorPoint The positioning point for the menu. Can be set by the context menu trigger or the button trigger. | Point | |
aria-label The accessibility label for the menu | string | |
closeOnSelect Whether to close the menu when an option is selected | boolean | |
dir The document's text/writing direction. | 'ltr' | 'rtl' | "ltr" |
getRootNode A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron. | () => Node | ShadowRoot | Document | |
highlightedId The `id` of the active menu item. | string | |
id The unique identifier of the machine. | string | |
ids The 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
}> | |
lazyMount Whether to enable lazy mounting | boolean | false |
loop Whether to loop the keyboard navigation. | boolean | |
onExitComplete Function called when the animation ends in the closed state. | () => void | |
onFocusOutside Function called when the focus is moved outside the component | (event: FocusOutsideEvent) => void | |
onInteractOutside Function called when an interaction happens outside the component | (event: InteractOutsideEvent) => void | |
onOpenChange Function called when the menu opens or closes | (details: OpenChangeDetails) => void | |
onPointerDownOutside Function called when the pointer is pressed down outside the component | (event: PointerDownOutsideEvent) => void | |
onSelect Function called when a menu item is selected. | (details: SelectionDetails) => void | |
onValueChange Callback to be called when the menu values change (for radios and checkboxes). | (details: ValueChangeDetails) => void | |
open Whether the menu is open | boolean | |
positioning The options used to dynamically position the menu | PositioningOptions | |
present Whether the node is present (controlled by the user) | boolean | |
unmountOnExit Whether to unmount on exit. | boolean | false |
value The values of radios and checkboxes in the menu. | Record<string, string | string[]> |
Prop | Type | Default |
---|---|---|
id The `id` of the menu item option. | string | |
asChild Render as a different element type. | boolean | |
closeOnSelect Whether the menu should be closed when the option is selected. | boolean | |
disabled Whether the menu item is disabled | boolean | |
valueText 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. | string |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
id The `id` of the element that provides accessibility label to the option group | string | |
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
name The option's name as specified in menu's `context.values` object | string | |
type Whether the option is a radio or a checkbox | 'checkbox' | 'radio' | |
value The value of the option | string | |
asChild Render as a different element type. | boolean | |
closeOnSelect Whether the menu should be closed when the option is selected. | boolean | |
disabled Whether the menu item is disabled | boolean | |
onCheckedChange Function called when the option state is changed | (checked: boolean) => void | |
valueText 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. | string |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
asChild Render as a different element type. | boolean |
Prop | Type | Default |
---|---|---|
htmlFor | string | |
asChild Render as a different element type. | boolean |
Previous
Hover CardNext
Number Input