Skip to content

DropdownMenu

Click-triggered menu of actions. Built on Reka UI's DropdownMenu primitives — keyboard-navigable, typeahead-aware, with focus management out of the box.

bash
npm install @vuecs/overlays
vue
<script setup lang="ts">
import {
    VCDropdownMenu,
    VCDropdownMenuContent,
    VCDropdownMenuGroup,
    VCDropdownMenuItem,
    VCDropdownMenuLabel,
    VCDropdownMenuSeparator,
    VCDropdownMenuTrigger,
} from '@vuecs/overlays';
import { ref } from 'vue';

const lastAction = ref<string>('—');
</script>

<template>
    <VCDropdownMenu>
        <VCDropdownMenuTrigger>Actions ▾</VCDropdownMenuTrigger>
        <VCDropdownMenuContent>
            <VCDropdownMenuLabel>Manage</VCDropdownMenuLabel>
            <VCDropdownMenuGroup>
                <VCDropdownMenuItem @select="lastAction = 'edit'">Edit</VCDropdownMenuItem>
                <VCDropdownMenuItem @select="lastAction = 'duplicate'">Duplicate</VCDropdownMenuItem>
                <VCDropdownMenuItem @select="lastAction = 'share'">Share</VCDropdownMenuItem>
            </VCDropdownMenuGroup>
            <VCDropdownMenuSeparator />
            <VCDropdownMenuItem @select="lastAction = 'archive'">Archive</VCDropdownMenuItem>
            <VCDropdownMenuItem @select="lastAction = 'delete'">Delete</VCDropdownMenuItem>
        </VCDropdownMenuContent>
    </VCDropdownMenu>
    <p>Last action: {{ lastAction }}</p>
</template>
css
@import "tailwindcss";
@import "@vuecs/design";

@custom-variant dark (&:where(.dark, .dark *));

Compound API

ComponentWrapsNotes
VCDropdownMenuDropdownMenuRootHolds open state. v-models open.
VCDropdownMenuTriggerDropdownMenuTriggerButton that opens the menu.
VCDropdownMenuContentDropdownMenuPortal + DropdownMenuContentMenu panel. Position via side / align. loop controls keyboard wrap (default true).
VCDropdownMenuItemDropdownMenuItemSingle action. Emits select.
VCDropdownMenuCheckboxItemDropdownMenuCheckboxItemToggleable item. v-models modelValue (true / false / 'indeterminate').
VCDropdownMenuRadioGroupDropdownMenuRadioGroupWraps a group of VCDropdownMenuRadioItem. v-models modelValue (the selected value).
VCDropdownMenuRadioItemDropdownMenuRadioItemOne radio choice; takes a required value.
VCDropdownMenuItemIndicatorDropdownMenuItemIndicatorRenders inside CheckboxItem / RadioItem only when checked. Default content is .
VCDropdownMenuLabelDropdownMenuLabelSection heading.
VCDropdownMenuSeparatorDropdownMenuSeparatorHorizontal divider.
VCDropdownMenuGroupDropdownMenuGroupWrapping group (semantic).
VCDropdownMenuSubDropdownMenuSubNested-menu root; v-models open.
VCDropdownMenuSubTriggerDropdownMenuSubTriggerItem that opens a nested menu (renders inside the parent Content).
VCDropdownMenuSubContentDropdownMenuPortal + DropdownMenuSubContentNested-menu panel.
VCDropdownMenuArrowDropdownMenuArrowOptional pointer arrow.
vue
<script setup lang="ts">
import {
    VCDropdownMenu,
    VCDropdownMenuContent,
    VCDropdownMenuGroup,
    VCDropdownMenuItem,
    VCDropdownMenuLabel,
    VCDropdownMenuSeparator,
    VCDropdownMenuTrigger,
} from '@vuecs/overlays';

const onAction = (key: string) => console.log('action:', key);
</script>

<template>
    <VCDropdownMenu>
        <VCDropdownMenuTrigger>Actions</VCDropdownMenuTrigger>
        <VCDropdownMenuContent>
            <VCDropdownMenuLabel>Manage</VCDropdownMenuLabel>
            <VCDropdownMenuGroup>
                <VCDropdownMenuItem @select="onAction('edit')">Edit</VCDropdownMenuItem>
                <VCDropdownMenuItem @select="onAction('share')">Share</VCDropdownMenuItem>
            </VCDropdownMenuGroup>
            <VCDropdownMenuSeparator />
            <VCDropdownMenuItem @select="onAction('delete')">Delete</VCDropdownMenuItem>
        </VCDropdownMenuContent>
    </VCDropdownMenu>
</template>

Theme keys

KeyDefault classNotes
triggervc-dropdown-trigger
contentvc-dropdown-contentMenu panel; data-state="open|closed" for animation.
itemvc-dropdown-itemSingle entry; data-highlighted while hovered/focused, data-disabled when disabled.
labelvc-dropdown-label
separatorvc-dropdown-separator
groupvc-dropdown-group
arrowvc-dropdown-arrow

Accessibility

Provided by Reka:

  • Arrow-key navigation between items, with loop wrap support
  • Typeahead — letters jump to the next item starting with that character
  • Escape closes; click-outside dismisses
  • Focus returns to the trigger on close
  • ARIA role="menu" / role="menuitem" with aria-orientation

Animations

Both theme-tailwind and theme-bootstrap ship enter and exit animations (fade + zoom-95) on the menu panel and nested subContent via @vuecs/design's vanilla-CSS port of tw-animate-css. Tailwind uses data-[state=open]: / data-[state=closed]: variant prefixes; BS5 uses the dual-state helper class vc-overlay-anim. Reka's MenuContent (the primitive backing both DropdownMenu and ContextMenu) wraps with Presence so exit animations play before unmount. prefers-reduced-motion: reduce disables every animation.

Theme keys for extras

KeyDefault classNotes
checkboxItemvc-dropdown-checkbox-itemWider left padding to leave room for the indicator.
radioItemvc-dropdown-radio-itemSame shape as checkboxItem.
radioGroupvc-dropdown-radio-groupWrapping group (semantic).
itemIndicatorvc-dropdown-item-indicatorAbsolute-positioned indicator inside checkboxItem/radioItem.
subTriggervc-dropdown-sub-triggerItem that opens a nested menu; gets data-state="open" while open.
subContentvc-dropdown-sub-contentNested-menu panel; same data-state animation hooks as content.

API Reference

<VCDropdownMenu>

Root component. Wraps DropdownMenuRoot.

PropTypeDefaultDescription
openboolean | undefinedundefinedControlled open state.
defaultOpenbooleanfalseInitial open state for uncontrolled usage.
modalbooleantrueBlock interaction with content outside the menu while open.
dir'ltr' | 'rtl' | undefinedundefinedReading direction. Falls back to the inherited useConfig('dir') value.
EmitPayloadDescription
update:openbooleanFired on open/close.

<VCDropdownMenuTrigger>

Toggles the menu. Wraps DropdownMenuTrigger.

PropTypeDefaultDescription
asstring'button'HTML tag to render.
asChildbooleanfalseRender via the default slot's child element.
themeClassPartial<DropdownMenuThemeClasses>undefinedPer-instance theme override.
themeVariantRecord<string, string | boolean>undefinedPer-instance variant values.

<VCDropdownMenuContent>

Floating menu panel. Bundles DropdownMenuPortal + DropdownMenuContent.

PropTypeDefaultDescription
inlinebooleanfalseSkip the portal.
side'top' | 'right' | 'bottom' | 'left''bottom'Preferred side relative to the trigger.
sideOffsetnumber4Distance in pixels between trigger and panel.
align'start' | 'center' | 'end''start'Alignment along the chosen side.
alignOffsetnumber0Offset in pixels along the alignment axis.
avoidCollisionsbooleantrueFlip / shift to stay inside the viewport.
loopbooleantrueWrap arrow-key focus from last item back to first.
themeClassPartial<DropdownMenuThemeClasses>undefinedPer-instance theme override.
themeVariantRecord<string, string | boolean>undefinedPer-instance variant values.

<VCDropdownMenuItem>

Single menu entry. Wraps DropdownMenuItem.

PropTypeDefaultDescription
asstring'div'HTML tag to render.
asChildbooleanfalseRender via the default slot's child element.
disabledbooleanfalseMark the item as disabled (skipped by keyboard navigation).
textValuestring | undefinedundefinedOverride the typeahead match text (defaults to the item's textContent).
themeClassPartial<DropdownMenuThemeClasses>undefinedPer-instance theme override.
themeVariantRecord<string, string | boolean>undefinedPer-instance variant values.
EmitPayloadDescription
selectEventFired when the item is activated (click or Enter). Call event.preventDefault() to keep the menu open.

<VCDropdownMenuCheckboxItem>

Toggleable item. Wraps DropdownMenuCheckboxItem. Render <VCDropdownMenuItemIndicator> inside to show the check.

PropTypeDefaultDescription
modelValueboolean | 'indeterminate' | undefinedundefinedCurrent checked state. Use v-model to bind.
disabledbooleanfalseDisabled flag.
textValuestring | undefinedundefinedOverride the typeahead match text.
themeClassPartial<DropdownMenuThemeClasses>undefinedPer-instance theme override.
themeVariantRecord<string, string | boolean>undefinedPer-instance variant values.
EmitPayloadDescription
update:modelValuebooleanFired on toggle.
selectEventFired when activated. Call event.preventDefault() to keep the menu open.

<VCDropdownMenuRadioGroup>

Wraps a group of <VCDropdownMenuRadioItem> and tracks the selected value. Wraps DropdownMenuRadioGroup.

PropTypeDefaultDescription
modelValuestring | undefinedundefinedSelected value. Use v-model to bind.
themeClassPartial<DropdownMenuThemeClasses>undefinedPer-instance theme override.
themeVariantRecord<string, string | boolean>undefinedPer-instance variant values.
EmitPayloadDescription
update:modelValuestringFired when a radio item is selected.

<VCDropdownMenuRadioItem>

One radio choice inside a <VCDropdownMenuRadioGroup>. Wraps DropdownMenuRadioItem.

PropTypeDefaultDescription
valuestring(required)Value emitted to the parent group when selected.
disabledbooleanfalseDisabled flag.
textValuestring | undefinedundefinedOverride the typeahead match text.
themeClassPartial<DropdownMenuThemeClasses>undefinedPer-instance theme override.
themeVariantRecord<string, string | boolean>undefinedPer-instance variant values.
EmitPayloadDescription
selectEventFired when activated.

<VCDropdownMenuItemIndicator>

Renders inside <VCDropdownMenuCheckboxItem> / <VCDropdownMenuRadioItem> only when checked / selected. Default content is . Wraps DropdownMenuItemIndicator.

PropTypeDefaultDescription
forceMountbooleanfalseRender even when not checked (useful for animation lifecycle).
themeClassPartial<DropdownMenuThemeClasses>undefinedPer-instance theme override.
themeVariantRecord<string, string | boolean>undefinedPer-instance variant values.

<VCDropdownMenuLabel>

Non-interactive section heading. Wraps DropdownMenuLabel.

PropTypeDefaultDescription
asstring'div'HTML tag to render.
asChildbooleanfalseRender via the default slot's child element.
themeClassPartial<DropdownMenuThemeClasses>undefinedPer-instance theme override.
themeVariantRecord<string, string | boolean>undefinedPer-instance variant values.

<VCDropdownMenuSeparator>

Horizontal divider between items. Wraps DropdownMenuSeparator. No props beyond the theme overrides.

<VCDropdownMenuGroup>

Semantic wrapping group (renders a <div role="group">). Wraps DropdownMenuGroup. No props beyond the theme overrides.

<VCDropdownMenuSub>

Nested-menu root. Wraps DropdownMenuSub.

PropTypeDefaultDescription
openboolean | undefinedundefinedControlled open state.
defaultOpenbooleanfalseInitial open state.
EmitPayloadDescription
update:openbooleanFired on open/close.

<VCDropdownMenuSubTrigger>

Menu item that opens a nested menu when activated. Renders inside the parent Content. Wraps DropdownMenuSubTrigger.

PropTypeDefaultDescription
asstring'div'HTML tag to render.
asChildbooleanfalseRender via the default slot's child element.
disabledbooleanfalseDisabled flag.
textValuestring | undefinedundefinedOverride the typeahead match text.
themeClassPartial<DropdownMenuThemeClasses>undefinedPer-instance theme override.
themeVariantRecord<string, string | boolean>undefinedPer-instance variant values.

<VCDropdownMenuSubContent>

Nested-menu panel. Bundles DropdownMenuPortal + DropdownMenuSubContent.

PropTypeDefaultDescription
inlinebooleanfalseSkip the portal.
sideOffsetnumber0Distance in pixels between sub-trigger and sub-panel.
alignOffsetnumber0Offset in pixels along the alignment axis.
avoidCollisionsbooleantrueFlip / shift to stay inside the viewport.
loopbooleantrueWrap arrow-key focus inside the sub-menu.
themeClassPartial<DropdownMenuThemeClasses>undefinedPer-instance theme override.
themeVariantRecord<string, string | boolean>undefinedPer-instance variant values.

<VCDropdownMenuArrow>

Optional pointer arrow following the menu's anchor. Wraps DropdownMenuArrow.

PropTypeDefaultDescription
widthnumber10Arrow width in pixels.
heightnumber5Arrow height in pixels.
themeClassPartial<DropdownMenuThemeClasses>undefinedPer-instance theme override.
themeVariantRecord<string, string | boolean>undefinedPer-instance variant values.

Released under the Apache 2.0 License.