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
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/menu'
export const Basic = () => (
<Menu.Root>
<Menu.Trigger>
Open menu <Menu.Indicator>➡️</Menu.Indicator>
</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
import { Menu } from '@ark-ui/solid/menu'
export const Basic = () => (
<Menu.Root>
<Menu.Trigger>
Open menu <Menu.Indicator>➡️</Menu.Indicator>
</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
<script setup lang="ts">
import { Menu } from '@ark-ui/vue/menu'
</script>
<template>
<Menu.Root>
<Menu.Trigger>
Open menu
<Menu.Indicator>➡️</Menu.Indicator>
</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</template>
Listening to item selection
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/menu'
import { useState } from 'react'
export const Controlled = () => {
const [isOpen, setIsOpen] = useState(false)
return (
<>
<button type="button" onClick={() => setIsOpen(!isOpen)}>
Trigger from the outside
</button>
<Menu.Root open={isOpen}>
<Menu.Trigger>
Open menu <Menu.Indicator>➡️</Menu.Indicator>
</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</>
)
}
import { Menu } from '@ark-ui/solid/menu'
import { createSignal } from 'solid-js'
export const Controlled = () => {
const [isOpen, setIsOpen] = createSignal(false)
return (
<>
<button type="button" onClick={() => setIsOpen(!isOpen())}>
Trigger from the outside
</button>
<Menu.Root open={isOpen()} onSelect={(id) => console.log(id)}>
<Menu.Trigger>
Open menu <Menu.Indicator>➡️</Menu.Indicator>
</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</>
)
}
<script setup lang="ts">
import { Menu } from '@ark-ui/vue/menu'
import { ref } from 'vue'
const isOpen = ref(false)
</script>
<template>
<button @click="isOpen = !isOpen">Trigger from the outside</button>
<Menu.Root :open="isOpen">
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</template>
Grouping menu items
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/menu'
export const Group = () => (
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.ItemGroup>
<Menu.ItemGroupLabel>JS Frameworks</Menu.ItemGroupLabel>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.ItemGroup>
<Menu.ItemGroup>
<Menu.ItemGroupLabel>CSS Frameworks</Menu.ItemGroupLabel>
<Menu.Item value="panda">Panda</Menu.Item>
<Menu.Item value="tailwind">Tailwind</Menu.Item>
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
import { Menu } from '@ark-ui/solid/menu'
export const Group = () => (
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.ItemGroup>
<Menu.ItemGroupLabel>JS Frameworks</Menu.ItemGroupLabel>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.ItemGroup>
<Menu.ItemGroup>
<Menu.ItemGroupLabel>CSS Frameworks</Menu.ItemGroupLabel>
<Menu.Item value="panda">Panda</Menu.Item>
<Menu.Item value="tailwind">Tailwind</Menu.Item>
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
<script setup lang="ts">
import { Menu } from '@ark-ui/vue/menu'
</script>
<template>
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.ItemGroup>
<Menu.ItemGroupLabel>JS Frameworks</Menu.ItemGroupLabel>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.ItemGroup>
<Menu.ItemGroup>
<Menu.ItemGroupLabel>CSS Frameworks</Menu.ItemGroupLabel>
<Menu.Item value="panda">Panda</Menu.Item>
<Menu.Item value="tailwind">Tailwind</Menu.Item>
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</template>
Separating menu items
To separate menu items, render the Menu.Separator
component.
import { Menu } from '@ark-ui/react/menu'
export const Separator = () => (
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Separator />
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
import { Menu } from '@ark-ui/solid/menu'
export const Separator = () => (
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Separator />
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
<script setup lang="ts">
import { Menu } from '@ark-ui/vue/menu'
</script>
<template>
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Separator />
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</template>
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.
import { Menu } from '@ark-ui/react/menu'
export const Context = () => (
<Menu.Root>
<Menu.ContextTrigger>Right click me</Menu.ContextTrigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
import { Menu } from '@ark-ui/solid/menu'
export const Context = () => (
<Menu.Root>
<Menu.ContextTrigger>Right click me</Menu.ContextTrigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
<script setup lang="ts">
import { Menu } from '@ark-ui/vue/menu'
</script>
<template>
<Menu.Root>
<Menu.ContextTrigger>Right click me</Menu.ContextTrigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</template>
Nested menu
To show a nested menu, render another Menu
component and use the Menu.TriggerItem
component to
open the submenu.
import { Menu } from '@ark-ui/react/menu'
import { Portal } from '@ark-ui/react/portal'
export const Nested = () => (
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Root>
<Menu.TriggerItem>JS Frameworks</Menu.TriggerItem>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
<Menu.Root>
<Menu.TriggerItem>CSS Frameworks</Menu.TriggerItem>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="panda">Panda</Menu.Item>
<Menu.Item value="tailwind">Tailwind</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
import { Menu } from '@ark-ui/solid/menu'
import { Portal } from 'solid-js/web'
export const Nested = () => (
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Root>
<Menu.TriggerItem>JS Frameworks</Menu.TriggerItem>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
<Menu.Root>
<Menu.TriggerItem>CSS Frameworks</Menu.TriggerItem>
<Portal>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="panda">Panda</Menu.Item>
<Menu.Item value="tailwind">Tailwind</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Portal>
</Menu.Root>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
<script setup lang="ts">
import { Menu } from '@ark-ui/vue/menu'
</script>
<template>
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.Root>
<Menu.TriggerItem>JS Frameworks</Menu.TriggerItem>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="react">React</Menu.Item>
<Menu.Item value="solid">Solid</Menu.Item>
<Menu.Item value="vue">Vue</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
<Menu.Root>
<Menu.TriggerItem>CSS Frameworks</Menu.TriggerItem>
<Menu.Positioner>
<Menu.Content>
<Menu.Item value="panda">Panda</Menu.Item>
<Menu.Item value="tailwind">Tailwind</Menu.Item>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</template>
Checkbox
To add a checkbox to a menu item, use the Menu.Checkbox
component.
import { Menu } from '@ark-ui/react/menu'
import { useState } from 'react'
export const Checkbox = () => {
const [checked, setChecked] = useState(false)
return (
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.CheckboxItem checked={checked} onCheckedChange={setChecked} value="checked">
<Menu.ItemIndicator>✅</Menu.ItemIndicator>
<Menu.ItemText>Check me</Menu.ItemText>
</Menu.CheckboxItem>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
}
import { Menu } from '@ark-ui/solid/menu'
import { createSignal } from 'solid-js'
export const Checkbox = () => {
const [checked, setChecked] = createSignal(true)
return (
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.CheckboxItem checked={checked()} onCheckedChange={setChecked} value="checked">
<Menu.ItemIndicator>✅</Menu.ItemIndicator>
<Menu.ItemText>Check me</Menu.ItemText>
</Menu.CheckboxItem>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
}
<script setup lang="ts">
import { Menu } from '@ark-ui/vue/menu'
import { ref } from 'vue'
const checked = ref(true)
</script>
<template>
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.CheckboxItem v-model:checked="checked" value="my-checkbox">
<Menu.ItemIndicator>✅</Menu.ItemIndicator>
<Menu.ItemText>Check me</Menu.ItemText>
</Menu.CheckboxItem>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</template>
Radio Group
To group radio option items, use the Menu.RadioGroup
component.
import { Menu } from '@ark-ui/react/menu'
import { useState } from 'react'
export const RadioGroup = () => {
const [value, setValue] = useState('React')
return (
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.RadioItemGroup value={value} onValueChange={(e) => setValue(e.value)}>
<Menu.ItemGroupLabel>JS Frameworks</Menu.ItemGroupLabel>
{['React', 'Solid', 'Vue'].map((framework) => (
<Menu.RadioItem key={framework} value={framework}>
<Menu.ItemIndicator>✅</Menu.ItemIndicator>
<Menu.ItemText>{framework}</Menu.ItemText>
</Menu.RadioItem>
))}
</Menu.RadioItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
}
import { Menu } from '@ark-ui/solid/menu'
import { Index, createSignal } from 'solid-js'
export const RadioGroup = () => {
const [value, setValue] = createSignal('React')
return (
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.RadioItemGroup value={value()} onValueChange={(e) => setValue(e.value)}>
<Menu.ItemGroupLabel>JS Frameworks</Menu.ItemGroupLabel>
<Index each={['React', 'Solid', 'Svelte', 'Vue']}>
{(framework) => (
<Menu.RadioItem value={framework()} disabled={framework() === 'Svelte'}>
<Menu.ItemIndicator>✅</Menu.ItemIndicator>
<Menu.ItemText>{framework()}</Menu.ItemText>
</Menu.RadioItem>
)}
</Index>
</Menu.RadioItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
)
}
<script setup lang="ts">
import { Menu } from '@ark-ui/vue/menu'
import { ref } from 'vue'
const value = ref('React')
const items = ref(['React', 'Solid', 'Vue'])
</script>
<template>
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Positioner>
<Menu.Content>
<Menu.RadioItemGroup v-model="value">
<Menu.ItemGroupLabel>JS Frameworks</Menu.ItemGroupLabel>
<Menu.RadioItem v-for="item in items" :key="item" :value="item">
<Menu.ItemIndicator>✅</Menu.ItemIndicator>
<Menu.ItemText>{{ item }}</Menu.ItemText>
</Menu.RadioItem>
</Menu.RadioItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>
</template>
API Reference
Accessibility
Complies with the Menu WAI-ARIA design pattern.
Keyboard Support
Key | Description |
---|---|
Space | Activates/Selects the highlighted item |
Enter | Activates/Selects the highlighted item |
ArrowDown | Highlights the next item in the menu |
ArrowUp | Highlights the previous item in the menu |
ArrowRightArrowLeft | When focus is on trigger, opens or closes the submenu depending on reading direction. |
Esc | Closes the menu and moves focus to the trigger |