Menu
A menu of actions.
Anatomy
<script setup>
import { Menu } from '@shardsui/vue/menu'
</script>
<template>
<Menu.Root>
<Menu.Trigger />
<Menu.Portal>
<Menu.Backdrop />
<Menu.Positioner>
<Menu.Popup>
<Menu.Arrow />
<Menu.Item />
<Menu.LinkItem />
<Menu.Separator />
<Menu.SubmenuRoot>
<Menu.SubmenuTrigger />
</Menu.SubmenuRoot>
<Menu.Group>
<Menu.GroupLabel />
</Menu.Group>
<Menu.RadioGroup>
<Menu.GroupLabel />
<Menu.RadioItem>
<Menu.RadioItemIndicator />
</Menu.RadioItem>
</Menu.RadioGroup>
<Menu.CheckboxItem>
<Menu.CheckboxItemIndicator />
</Menu.CheckboxItem>
<Menu.Viewport />
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
</template>Examples
Open on hover
Add the openOnHover prop to <Menu.Trigger> to open the menu on pointer hover. Tune the timing with delay (how long the pointer must rest before it opens) and closeDelay (how long it lingers after the pointer leaves), both in milliseconds.
Checkbox items
<Menu.CheckboxItem> renders a menu item that toggles a setting on or off.
Radio items
<Menu.RadioGroup> and <Menu.RadioItem> turn a set of items into mutually exclusive choices, like radio buttons.
Close on click
Set closeOnClick to decide whether clicking an item dismisses the menu.
<template>
<!-- Close the menu when a checkbox item is clicked -->
<Menu.CheckboxItem close-on-click />
<!-- Keep the menu open when an item is clicked -->
<Menu.Item :close-on-click="false" />
</template>Group labels
<Menu.GroupLabel> gives a <Menu.Group> or <Menu.RadioGroup> a heading.
Nested menu
Nest another menu inside the current one with <Menu.SubmenuRoot>, and mark the item that opens it with <Menu.SubmenuTrigger>.
<template>
<Menu.Root>
<Menu.Trigger />
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
<Menu.Arrow />
<Menu.Item />
<!-- Submenu -->
<Menu.SubmenuRoot>
<Menu.SubmenuTrigger />
<Menu.Positioner>
<Menu.Popup>
<!-- Submenu items -->
</Menu.Popup>
</Menu.Positioner>
</Menu.SubmenuRoot>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
</template>Navigate to another page
<Menu.LinkItem> renders a menu item as a link.
<template>
<Menu.LinkItem href="/projects">Go to Projects</Menu.LinkItem>
</template>Open a dialog
To open a dialog from a menu, hold the dialog's open state yourself and flip it from the item's @click handler.
<script setup>
import { Dialog } from '@shardsui/vue/dialog'
import { Menu } from '@shardsui/vue/menu'
import { shallowRef } from 'vue'
const dialogOpen = shallowRef(false)
</script>
<template>
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
<!-- Open the dialog when the menu item is clicked -->
<Menu.Item @click="dialogOpen = true">Open dialog</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
<!-- Control the dialog state -->
<Dialog.Root v-model:open="dialogOpen">
<Dialog.Portal>
<Dialog.Backdrop />
<Dialog.Popup>
<!-- Rest of the dialog -->
</Dialog.Popup>
</Dialog.Portal>
</Dialog.Root>
</template>Detached triggers
A menu's trigger can sit inside <Menu.Root> (as in the hero demo above) or somewhere else entirely. When it lives outside, create a handle with Menu.createHandle() and pass it to both the trigger and the root.
Only top-level menus support detached triggers; a submenu's trigger always stays inside its SubmenuRoot.
<!-- [!code word::handle="demoMenu"] -->
<script setup>
import { Menu } from '@shardsui/vue/menu'
const demoMenu = Menu.createHandle()
</script>
<template>
<Menu.Trigger :handle="demoMenu">Actions</Menu.Trigger>
<Menu.Root :handle="demoMenu">
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
<Menu.Item>Edit</Menu.Item>
<Menu.Item>Share</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
</template>Multiple triggers
Several triggers can open the same menu. Render more than one <Menu.Trigger> inside a single <Menu.Root>, or point several detached triggers at the same handle.
<template>
<Menu.Root>
<Menu.Trigger>Row actions</Menu.Trigger>
<Menu.Trigger>Quick actions</Menu.Trigger>
<!-- Rest of the menu -->
</Menu.Root>
</template><script setup>
const projectMenu = Menu.createHandle()
</script>
<template>
<Menu.Trigger :handle="projectMenu">Row actions</Menu.Trigger>
<Menu.Trigger :handle="projectMenu">Quick actions</Menu.Trigger>
<Menu.Root :handle="projectMenu">
<!-- Rest of the menu -->
</Menu.Root>
</template>A menu can show different content depending on which trigger opened it. Give each <Menu.Trigger> a payload prop and read it from the payload argument of <Menu.Root>'s default slot (typed by the handle's type argument).
<script setup lang="ts">
import { Menu } from '@shardsui/vue/menu'
const menus = {
course: ['Rename', 'Duplicate', 'Archive'],
lesson: ['Add note', 'Bookmark', 'Share']
}
const demoMenu = Menu.createHandle<keyof typeof menus>()
</script>
<template>
<Menu.Trigger :handle="demoMenu" :payload="'course'">Course</Menu.Trigger>
<Menu.Trigger :handle="demoMenu" :payload="'lesson'">Lesson</Menu.Trigger>
<Menu.Root v-slot="{ payload }" :handle="demoMenu">
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
<Menu.Viewport>
<template v-if="payload">
<Menu.Item v-for="item in menus[payload]" :key="item">{{ item }}</Menu.Item>
</template>
</Menu.Viewport>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
</template>Controlled mode with multiple triggers
Drive the open state yourself with v-model:open on <Menu.Root>. With several triggers, track the active one through v-model:trigger-id on <Menu.Root> and a matching id on each <Menu.Trigger>: writing an id to the model selects that trigger.
Arrow
<Menu.Arrow> renders an arrow inside the popup that points at the trigger.
Animating the Menu
When several detached triggers share one menu, its position, size, and content can animate as it travels between them.
Position and Size
For position, transition the left, right, top, and bottom properties of the Positioner part. For size, transition the width and height of the Popup part.
Content
When different triggers swap what the menu shows, wrap the content in a <Menu.Viewport> to animate the change. It renders a div carrying data-activation-direction — a space-separated horizontal and vertical pair such as right down — so your animation can lean toward the direction of travel. Match a single token with the ~= attribute selector, such as [data-activation-direction~='right'].
Within <Menu.Viewport>, each piece of content sits in a div tagged with a transition data attribute:
data-current: the content on screen when nothing is transitioning, or the incoming content during one.data-previous: the outgoing content during a transition.
API reference
Root
Groups all parts of the menu. Doesn't render its own HTML element.
Trigger
A button that opens the menu.
Renders a <button> element.
Backdrop
An overlay displayed beneath the popup.
Renders a <div> element.
Portal
A portal that moves the popup out to <body>, clear of ancestor clipping and stacking.
Renders a <div> element.
Positioner
Positions the menu popup against the trigger.
Renders a <div> element.
Popup
A container for the menu items.
Renders a <div> element.
Viewport
A viewport for displaying content transitions.
Only needed when one popup has multiple triggers, its content changes with the trigger, and the
switch is animated.
Renders a <div> element.
Arrow
Displays an element positioned against the anchor.
Renders a <div> element.
Item
An individual interactive item in the menu.
Renders a <div> element.
LinkItem
A link in the menu, for navigating to a different page or section.
Renders an <a> element.
SubmenuRoot
Groups all parts of a submenu. Doesn't render its own HTML element.
SubmenuTrigger
A menu item that opens a submenu.
Renders a <div> element.
Group
Groups related menu items with the corresponding label.
Renders a <div> element.
GroupLabel
An accessible label that is automatically associated with its parent group.
Renders a <div> element.
RadioGroup
Groups related radio items, labelled by a nested <Menu.GroupLabel>.
Renders a <div> element.
RadioItem
A menu item that works like a radio button in a given group.
Renders a <div> element.
RadioItemIndicator
Indicates whether the radio item is selected.
Renders a <span> element.
CheckboxItem
A menu item that toggles a setting on or off.
Renders a <div> element.
CheckboxItemIndicator
Indicates whether the checkbox item is ticked.
Renders a <span> element.
Separator
A separator element accessible to screen readers.
Renders a <div> element.
Handle
Connects a <Menu.Root> with detached <Menu.Trigger> components, and controls the menu imperatively. Pass a type argument to type the payload.
const menu = Menu.createHandle<Payload>()