Skip to content

ContextMenu

Right-click-triggered menu. Same shape as DropdownMenu but anchored to the cursor on contextmenu events. Built on Reka UI's ContextMenu primitives.

bash
npm install @vuecs/overlays
vue
<script setup lang="ts">
import {
    VCContextMenu,
    VCContextMenuContent,
    VCContextMenuItem,
    VCContextMenuLabel,
    VCContextMenuSeparator,
    VCContextMenuTrigger,
} from '@vuecs/overlays';
import { ref } from 'vue';

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

<template>
    <VCContextMenu>
        <VCContextMenuTrigger>
            <div class="rounded-md border-2 border-dashed p-8">
                Right-click me
            </div>
        </VCContextMenuTrigger>
        <VCContextMenuContent>
            <VCContextMenuLabel>Item options</VCContextMenuLabel>
            <VCContextMenuItem @select="lastAction = 'open'">Open</VCContextMenuItem>
            <VCContextMenuItem @select="lastAction = 'rename'">Rename</VCContextMenuItem>
            <VCContextMenuItem @select="lastAction = 'duplicate'">Duplicate</VCContextMenuItem>
            <VCContextMenuSeparator />
            <VCContextMenuItem @select="lastAction = 'delete'">Delete</VCContextMenuItem>
        </VCContextMenuContent>
    </VCContextMenu>
    <p>Last action: {{ lastAction }}</p>
</template>
css
@import "tailwindcss";
@import "@vuecs/design";

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

Compound API

ComponentWrapsNotes
VCContextMenuContextMenuRootHolds open state. Emits update:open.
VCContextMenuTriggerContextMenuTriggerRight-click area. disabled to opt out.
VCContextMenuContentContextMenuPortal + ContextMenuContentMenu panel anchored to cursor. inline skips the portal.
VCContextMenuItemContextMenuItemSingle action. Emits select.
VCContextMenuLabelContextMenuLabelSection heading.
VCContextMenuSeparatorContextMenuSeparatorHorizontal divider.
VCContextMenuGroupContextMenuGroupWrapping group (semantic).
vue
<script setup lang="ts">
import {
    VCContextMenu,
    VCContextMenuContent,
    VCContextMenuItem,
    VCContextMenuSeparator,
    VCContextMenuTrigger,
} from '@vuecs/overlays';

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

<template>
    <VCContextMenu>
        <VCContextMenuTrigger>
            <div class="rounded-md border border-dashed p-8">
                Right-click me
            </div>
        </VCContextMenuTrigger>
        <VCContextMenuContent>
            <VCContextMenuItem @select="onAction('open')">Open</VCContextMenuItem>
            <VCContextMenuItem @select="onAction('rename')">Rename</VCContextMenuItem>
            <VCContextMenuSeparator />
            <VCContextMenuItem @select="onAction('delete')">Delete</VCContextMenuItem>
        </VCContextMenuContent>
    </VCContextMenu>
</template>

Theme keys

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

Accessibility

Same as DropdownMenu — keyboard arrow navigation, typeahead, Escape close, focus restoration. The trigger fires on long-press on touch devices, in addition to right-click on desktop.

Animations

Same as DropdownMenu — both theme-tailwind and theme-bootstrap ship enter and exit animations (fade + zoom-95) via @vuecs/design's vanilla-CSS port of tw-animate-css. Reka's shared MenuContent primitive wraps with Presence so exit animations play before unmount.

Extras

Same shape as DropdownMenuVCContextMenuCheckboxItem, VCContextMenuRadioGroup / VCContextMenuRadioItem, VCContextMenuItemIndicator, and VCContextMenuSub / VCContextMenuSubTrigger / VCContextMenuSubContent are all available with the same prop / event / theme-key shape (just vc-context-menu-* instead of vc-dropdown-*).

API Reference

<VCContextMenu>

Root component. Wraps ContextMenuRoot.

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

No open prop

ContextMenu has no open / defaultOpen prop because Reka triggers it from contextmenu events on <VCContextMenuTrigger>. Use update:open to observe the state.

<VCContextMenuTrigger>

Right-click area. Wraps ContextMenuTrigger.

PropTypeDefaultDescription
asstring'span'HTML tag to render.
asChildbooleanfalseRender via the default slot's child element.
disabledbooleanfalseDon't open the menu on right-click.
themeClassPartial<ContextMenuThemeClasses>undefinedPer-instance theme override.
themeVariantRecord<string, string | boolean>undefinedPer-instance variant values.

<VCContextMenuContent>

Floating menu panel anchored to the cursor. Bundles ContextMenuPortal + ContextMenuContent.

PropTypeDefaultDescription
inlinebooleanfalseSkip the portal.
loopbooleantrueWrap arrow-key focus from last item back to first.
avoidCollisionsbooleantrueFlip / shift to stay inside the viewport.
themeClassPartial<ContextMenuThemeClasses>undefinedPer-instance theme override.
themeVariantRecord<string, string | boolean>undefinedPer-instance variant values.

<VCContextMenuItem> / Label / Separator / Group / CheckboxItem / RadioGroup / RadioItem / ItemIndicator / Sub / SubTrigger / SubContent

Identical prop, event, and slot shape to the matching VCDropdownMenu* parts — see the DropdownMenu API Reference.

The only differences are:

  • The component prefix is VCContextMenu* instead of VCDropdownMenu*
  • Theme keys live under the contextMenu element (default classes use vc-context-menu-* instead of vc-dropdown-*)
  • The Reka primitive each part wraps is ContextMenu* instead of DropdownMenu*

Released under the Apache 2.0 License.