---
name: context-menu
title: Context Menu
url: https://ui.frontic.com/components/ui/context-menu.md
tier: ui
install: npx @frontic/ui add context-menu
files:
  - components/ui/context-menu/ContextMenu.vue
  - components/ui/context-menu/ContextMenuCheckboxItem.vue
  - components/ui/context-menu/ContextMenuContent.vue
  - components/ui/context-menu/ContextMenuGroup.vue
  - components/ui/context-menu/ContextMenuItem.vue
  - components/ui/context-menu/ContextMenuLabel.vue
  - components/ui/context-menu/ContextMenuPortal.vue
  - components/ui/context-menu/ContextMenuRadioGroup.vue
  - components/ui/context-menu/ContextMenuRadioItem.vue
  - components/ui/context-menu/ContextMenuSeparator.vue
  - components/ui/context-menu/ContextMenuShortcut.vue
  - components/ui/context-menu/ContextMenuSub.vue
  - components/ui/context-menu/ContextMenuSubContent.vue
  - components/ui/context-menu/ContextMenuSubTrigger.vue
  - components/ui/context-menu/ContextMenuTrigger.vue
  - components/ui/context-menu/index.ts
dependencies:
  - "@lucide/vue"
  - "@vueuse/core"
  - class-variance-authority
  - reka-ui
---

# Context Menu

A menu that opens on right-click, with actions, checkbox and radio items, labelled groups and destructive entries.

## Install

```sh
npx @frontic/ui add context-menu
```

npm packages: `@lucide/vue`, `@vueuse/core`, `class-variance-authority`, `reka-ui`

## Examples

```vue
<script setup lang="ts">
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger } from '@/components/ui/context-menu'
</script>

<template>
  <ContextMenu>
    <ContextMenuTrigger>Right-click the product</ContextMenuTrigger>
    <ContextMenuContent>
      <ContextMenuItem>Add to cart</ContextMenuItem>
      <ContextMenuItem>Save for later</ContextMenuItem>
      <ContextMenuSeparator />
      <ContextMenuItem color="destructive">Remove</ContextMenuItem>
    </ContextMenuContent>
  </ContextMenu>
</template>
```

## Anatomy

```vue
<ContextMenu>
  <ContextMenuTrigger />
  <ContextMenuContent>
    <ContextMenuItem />
    <ContextMenuSeparator />
  </ContextMenuContent>
</ContextMenu>
```

## API reference

### ContextMenu

Groups all parts of the context menu, which opens on right-click.

`data-slot="context-menu"` · `ContextMenu.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `pressOpenDelay` | `number` | `700` | The duration from when the trigger is pressed until the menu opens. |
| `open` | `boolean` |  | The controlled open state of the menu. Can be used as `v-model:open`. |
| `dir` | `Direction`: `'ltr' \| 'rtl'` |  | The reading direction of the combobox when applicable.<br><br>If omitted, inherits globally from `ConfigProvider` or assumes LTR (left-to-right) reading mode. |
| `modal` | `boolean` | `true` | The modality of the dropdown menu.<br><br>When set to `true`, interaction with outside elements will be disabled and only menu content will be visible to screen readers. |

| Event | Payload | Description |
| --- | --- | --- |
| `update:open` | `payload: boolean` | Fires with the new value when `open` changes; `v-model:open` binds it. |

### ContextMenuCheckboxItem

Lets the user switch an option on or off from the menu.

`data-slot="context-menu-checkbox-item"` · `ContextMenuCheckboxItem.vue`

Styling: `context-menu-item`, which extends [`menu-item`](https://ui.frontic.com/DESIGN.md#menu-item) and [`menu-item-destructive`](https://ui.frontic.com/DESIGN.md#menu-item-destructive). Change them there and every part that extends them follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `modelValue` | `CheckedState`: `boolean \| 'indeterminate'` | `false` | The controlled checked state of the item. Can be used as `v-model`. |
| `disabled` | `boolean` | `false` | When `true`, prevents the user from interacting with the item. |
| `textValue` | `string` |  | Optional text used for typeahead purposes. By default the typeahead behavior will use the `.textContent` of the item.<br><br>Use this when the content is complex, or you have non-textual content inside. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Event | Payload | Description |
| --- | --- | --- |
| `update:modelValue` | `payload: boolean` | Event handler called when the checked state changes. |
| `select` | `event: Event` | Event handler called when the user selects an item (via mouse or keyboard).<br><br>Calling `event.preventDefault` in this handler will prevent the menu from closing when selecting that item. |

| Slot | Slot props | Description |
| --- | --- | --- |
| `indicator-icon` |  | Replaces the check icon shown while the option is on. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-disabled` |  | Present while the part is disabled. |
| `data-highlighted` |  | Present while the item is highlighted by the pointer or the keyboard. |
| `data-state` | `'checked' \| 'unchecked' \| 'indeterminate'` | The state the part is in, one value per state. |

### ContextMenuContent

Holds the items the context menu shows while open.

`data-slot="context-menu-content"` · `ContextMenuContent.vue`

Styling: `context-menu-content`, which extends [`overlay`](https://ui.frontic.com/DESIGN.md#overlay). Change it there and every part that extends it follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `forceMount` | `boolean` | `false` | Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |
| `loop` | `boolean` | `false` | When `true`, keyboard navigation will loop from last item to first, and vice versa. |
| `reference` | `ReferenceElement`: `Element \| VirtualElement` |  | The custom element or virtual element that will be set as the reference to position the floating element.<br><br>If provided, it will replace the default anchor element. |
| `memoDependencies` | `unknown[]` |  | Reactive dependencies that should invalidate the memoized content subtree. |
| `sideFlip` | `boolean` | `true` | Flip to the opposite side when colliding with boundary. |
| `alignOffset` | `number` | `0` | An offset in pixels from the `start` or `end` alignment options. |
| `alignFlip` | `boolean` | `true` | Flip alignment when colliding with boundary. May only occur when `prioritizePosition` is true. |
| `avoidCollisions` | `boolean` | `true` | When `true`, overrides the side and align preferences to prevent collisions with boundary edges. |
| `collisionBoundary` | `Element \| (Element \| null)[] \| null`: `null \| Element \| (Element \| null)[]` | `[]` | The element used as the collision boundary. By default this is the viewport, though you can provide additional element(s) to be included in this check. |
| `collisionPadding` | `number \| Partial<Record<'top' \| 'right' \| 'bottom' \| 'left', number>>` | `0` | The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { top: 20, left: 20 }. |
| `hideShiftedArrow` | `boolean` | `true` | When `true`, hides the arrow when it cannot be centered to the reference element. |
| `sticky` | `'partial' \| 'always'` | `'partial'` | The sticky behavior on the align axis. `partial` will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. |
| `hideWhenDetached` | `boolean` | `false` | Whether to hide the content when the trigger becomes fully occluded. |
| `positionStrategy` | `'absolute' \| 'fixed'` |  | The type of CSS position property to use. |
| `disableUpdateOnLayoutShift` | `boolean` | `false` | Whether to disable the update position for the content when the layout shifted. |
| `prioritizePosition` | `boolean` | `false` | Force content to be position within the viewport.<br><br>Might overlap the reference element, which may not be desired. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Event | Payload | Description |
| --- | --- | --- |
| `escapeKeyDown` | `event: KeyboardEvent` | Event handler called when the escape key is down. Can be prevented. |
| `pointerDownOutside` | `event: PointerDownOutsideEvent` | Event handler called when a `pointerdown` event happens outside of the `DismissableLayer`. Can be prevented. |
| `focusOutside` | `event: FocusOutsideEvent` | Event handler called when the focus moves outside of the `DismissableLayer`. Can be prevented. |
| `interactOutside` | `event: PointerDownOutsideEvent \| FocusOutsideEvent` | Event handler called when an interaction happens outside the `DismissableLayer`. Specifically, when a `pointerdown` event happens outside or focus moves outside of it. Can be prevented. |
| `openAutoFocus` | `event: Event` | Fires when focus moves into the content as it opens; call `preventDefault()` to keep focus where it is. |
| `closeAutoFocus` | `event: Event` | Event handler called when auto-focusing on close. Can be prevented. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-align` | `'start' \| 'center' \| 'end'` | How the content lines up with its trigger: as `align` asks, unless there was no room. |
| `data-dismissable-layer` |  | Marks a layer that closes on Escape or a click outside it. |
| `data-side` | `'top' \| 'right' \| 'bottom' \| 'left'` | The side the content opened on: the one `side` asks for, unless there was no room. |
| `data-state` | `'open' \| 'closed'` | The state the part is in, one value per state. |

| CSS variable | Description |
| --- | --- |
| `--reka-context-menu-content-available-height` | The height the content has between its trigger and the edge of the screen. |
| `--reka-context-menu-content-available-width` | The width the content has between its trigger and the edge of the screen. |
| `--reka-context-menu-content-transform-origin` | The point the content grows from, on the side facing its trigger, for its open and close animation. |
| `--reka-context-menu-trigger-height` | The trigger's height. |
| `--reka-context-menu-trigger-width` | The trigger's width, to size the content to match. |

### ContextMenuGroup

Groups related items in the context menu.

`data-slot="context-menu-group"` · `ContextMenuGroup.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |

### ContextMenuItem

Holds one action the user can pick from the context menu.

`data-slot="context-menu-item"` · `ContextMenuItem.vue`

Styling: `context-menu-item`, which extends [`menu-item`](https://ui.frontic.com/DESIGN.md#menu-item) and [`menu-item-destructive`](https://ui.frontic.com/DESIGN.md#menu-item-destructive). Change them there and every part that extends them follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `disabled` | `boolean` | `false` | When `true`, prevents the user from interacting with the item. |
| `textValue` | `string` |  | Optional text used for typeahead purposes. By default the typeahead behavior will use the `.textContent` of the item.<br><br>Use this when the content is complex, or you have non-textual content inside. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
| `inset` | `boolean` | `false` | Indents the item to line up with items that show a check or icon. |
| `color` | `'neutral' \| 'destructive'` | `'neutral'` | The item's intent: destructive marks an action that deletes or cannot be undone, such as removing an address. |

| Event | Payload | Description |
| --- | --- | --- |
| `select` | `event: Event` | Event handler called when the user selects an item (via mouse or keyboard).<br><br>Calling `event.preventDefault` in this handler will prevent the menu from closing when selecting that item. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-color` | `'neutral' \| 'destructive'` | Carries the `color` prop. |
| `data-disabled` |  | Present while the part is disabled. |
| `data-highlighted` |  | Present while the item is highlighted by the pointer or the keyboard. |
| `data-inset` |  | Present when `inset` indents the item to line up with items that have an icon. |

### ContextMenuLabel

Labels a group of items in the context menu.

`data-slot="context-menu-label"` · `ContextMenuLabel.vue`

Styling: `context-menu-label`, which extends [`menu-label`](https://ui.frontic.com/DESIGN.md#menu-label). Change it there and every part that extends it follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
| `inset` | `boolean` | `false` | Indents the label to line up with items that show a check or icon. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-inset` |  | Present when `inset` indents the item to line up with items that have an icon. |

### ContextMenuPortal

Places the menu content above the rest of the page.

`data-slot="context-menu-portal"` · `ContextMenuPortal.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `to` | `string \| HTMLElement` |  | Vue native teleport component prop `:to`<br><br>[vuejs.org/guide/built-ins/teleport.html#basic-usage](https://vuejs.org/guide/built-ins/teleport.html#basic-usage) |
| `disabled` | `boolean` | `false` | Disable teleport and render the component inline<br><br>[vuejs.org/guide/built-ins/teleport.html#disabling-teleport](https://vuejs.org/guide/built-ins/teleport.html#disabling-teleport) |
| `defer` | `boolean` | `false` | Defer the resolving of a Teleport target until other parts of the application have mounted (requires Vue 3.5.0+)<br><br>[vuejs.org/guide/built-ins/teleport.html#deferred-teleport](https://vuejs.org/guide/built-ins/teleport.html#deferred-teleport) |
| `forceMount` | `boolean` | `false` | Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |

### ContextMenuRadioGroup

Groups radio items so the user can pick exactly one.

`data-slot="context-menu-radio-group"` · `ContextMenuRadioGroup.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `modelValue` | `AcceptableValue`: `null \| string \| number \| bigint \| Record<string, any>` |  | The value of the selected item in the group. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |

| Event | Payload | Description |
| --- | --- | --- |
| `update:modelValue` | `payload: AcceptableValue` | Event handler called when the value changes. |

### ContextMenuRadioItem

Lets the user choose one option from a group in the menu.

`data-slot="context-menu-radio-item"` · `ContextMenuRadioItem.vue`

Styling: `context-menu-item`, which extends [`menu-item`](https://ui.frontic.com/DESIGN.md#menu-item) and [`menu-item-destructive`](https://ui.frontic.com/DESIGN.md#menu-item-destructive). Change them there and every part that extends them follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` (required) | `AcceptableValue`: `null \| string \| number \| bigint \| Record<string, any>` |  | The unique value of the item. |
| `disabled` | `boolean` | `false` | When `true`, prevents the user from interacting with the item. |
| `textValue` | `string` |  | Optional text used for typeahead purposes. By default the typeahead behavior will use the `.textContent` of the item.<br><br>Use this when the content is complex, or you have non-textual content inside. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Event | Payload | Description |
| --- | --- | --- |
| `select` | `event: Event` | Event handler called when the user selects an item (via mouse or keyboard).<br><br>Calling `event.preventDefault` in this handler will prevent the menu from closing when selecting that item. |

| Slot | Slot props | Description |
| --- | --- | --- |
| `indicator-icon` |  | Replaces the check icon shown on the chosen option. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-disabled` |  | Present while the part is disabled. |
| `data-highlighted` |  | Present while the item is highlighted by the pointer or the keyboard. |
| `data-state` | `'checked' \| 'unchecked' \| 'indeterminate'` | The state the part is in, one value per state. |

### ContextMenuSeparator

Separates groups of items in the context menu.

`data-slot="context-menu-separator"` · `ContextMenuSeparator.vue`

Styling: `context-menu-separator`, which extends [`divider`](https://ui.frontic.com/DESIGN.md#divider). Change it there and every part that extends it follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### ContextMenuShortcut

Shows the keyboard shortcut for a menu item. Renders a `<span>`.

`data-slot="context-menu-shortcut"` · `ContextMenuShortcut.vue`

Styling: `context-menu-shortcut`, which extends [`menu-shortcut`](https://ui.frontic.com/DESIGN.md#menu-shortcut). Change it there and every part that extends it follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'span'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### ContextMenuSub

Groups a submenu trigger with the submenu it opens.

`data-slot="context-menu-sub"` · `ContextMenuSub.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `defaultOpen` | `boolean` | `false` | The open state of the submenu when it is initially rendered. Use when you do not need to control its open state. |
| `open` | `boolean` |  | The controlled open state of the menu. Can be used as `v-model:open`. |

| Event | Payload | Description |
| --- | --- | --- |
| `update:open` | `payload: boolean` | Event handler called when the open state of the submenu changes. |

### ContextMenuSubContent

Holds the items of a submenu while it is open.

`data-slot="context-menu-sub-content"` · `ContextMenuSubContent.vue`

Styling: `context-menu-sub-content`, which extends [`overlay`](https://ui.frontic.com/DESIGN.md#overlay). Change it there and every part that extends it follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `forceMount` | `boolean` | `false` | Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `loop` | `boolean` | `false` | When `true`, keyboard navigation will loop from last item to first, and vice versa. |
| `reference` | `ReferenceElement`: `Element \| VirtualElement` |  | The custom element or virtual element that will be set as the reference to position the floating element.<br><br>If provided, it will replace the default anchor element. |
| `memoDependencies` | `unknown[]` |  | Reactive dependencies that should invalidate the memoized content subtree. |
| `sideOffset` | `number` | `0` | The distance in pixels from the trigger. |
| `sideFlip` | `boolean` | `true` | Flip to the opposite side when colliding with boundary. |
| `alignOffset` | `number` | `0` | An offset in pixels from the `start` or `end` alignment options. |
| `alignFlip` | `boolean` | `true` | Flip alignment when colliding with boundary. May only occur when `prioritizePosition` is true. |
| `avoidCollisions` | `boolean` | `true` | When `true`, overrides the side and align preferences to prevent collisions with boundary edges. |
| `collisionBoundary` | `Element \| (Element \| null)[] \| null`: `null \| Element \| (Element \| null)[]` | `[]` | The element used as the collision boundary. By default this is the viewport, though you can provide additional element(s) to be included in this check. |
| `collisionPadding` | `number \| Partial<Record<'top' \| 'right' \| 'bottom' \| 'left', number>>` | `0` | The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { top: 20, left: 20 }. |
| `arrowPadding` | `number` | `0` | The padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. |
| `hideShiftedArrow` | `boolean` | `true` | When `true`, hides the arrow when it cannot be centered to the reference element. |
| `sticky` | `'partial' \| 'always'` | `'partial'` | The sticky behavior on the align axis. `partial` will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. |
| `hideWhenDetached` | `boolean` | `false` | Whether to hide the content when the trigger becomes fully occluded. |
| `positionStrategy` | `'absolute' \| 'fixed'` |  | The type of CSS position property to use. |
| `updatePositionStrategy` | `'always' \| 'optimized'` | `'optimized'` | Strategy to update the position of the floating element on every animation frame. |
| `disableUpdateOnLayoutShift` | `boolean` | `false` | Whether to disable the update position for the content when the layout shifted. |
| `prioritizePosition` | `boolean` | `false` | Force content to be position within the viewport.<br><br>Might overlap the reference element, which may not be desired. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Event | Payload | Description |
| --- | --- | --- |
| `escapeKeyDown` | `event: KeyboardEvent` | Event handler called when the escape key is down. Can be prevented. |
| `pointerDownOutside` | `event: PointerDownOutsideEvent` | Event handler called when a `pointerdown` event happens outside of the `DismissableLayer`. Can be prevented. |
| `focusOutside` | `event: FocusOutsideEvent` | Event handler called when the focus moves outside of the `DismissableLayer`. Can be prevented. |
| `interactOutside` | `event: PointerDownOutsideEvent \| FocusOutsideEvent` | Event handler called when an interaction happens outside the `DismissableLayer`. Specifically, when a `pointerdown` event happens outside or focus moves outside of it. Can be prevented. |
| `openAutoFocus` | `event: Event` | Fires when focus moves into the content as it opens; call `preventDefault()` to keep focus where it is. |
| `closeAutoFocus` | `event: Event` | Event handler called when auto-focusing on close. Can be prevented. |
| `entryFocus` | `event: Event` | Fires when keyboard focus enters the list of items; call `preventDefault()` to stop the first item taking it. |

| CSS variable | Description |
| --- | --- |
| `--reka-context-menu-content-available-height` | The height the content has between its trigger and the edge of the screen. |
| `--reka-context-menu-content-available-width` | The width the content has between its trigger and the edge of the screen. |
| `--reka-context-menu-content-transform-origin` | The point the content grows from, on the side facing its trigger, for its open and close animation. |
| `--reka-context-menu-trigger-height` | The trigger's height. |
| `--reka-context-menu-trigger-width` | The trigger's width, to size the content to match. |

### ContextMenuSubTrigger

Opens a submenu from an item of the context menu.

`data-slot="context-menu-sub-trigger"` · `ContextMenuSubTrigger.vue`

Styling: `context-menu-sub-trigger`, which extends [`menu-item`](https://ui.frontic.com/DESIGN.md#menu-item). Change it there and every part that extends it follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `graceDuration` | `number` | `300` | How long (in milliseconds) the pointer is given to reach the submenu after leaving the trigger. While it moves towards the submenu within this time, hovering other items won't close the submenu. Increase it for users who move the pointer slowly or on devices under heavy load. |
| `disabled` | `boolean` | `false` | When `true`, prevents the user from interacting with the item. |
| `textValue` | `string` |  | Optional text used for typeahead purposes. By default the typeahead behavior will use the `.textContent` of the item.<br><br>Use this when the content is complex, or you have non-textual content inside. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
| `inset` | `boolean` | `false` | Indents the trigger to line up with items that show a check or icon. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-disabled` |  | Present while the part is disabled. |
| `data-highlighted` |  | Present while the item is highlighted by the pointer or the keyboard. |
| `data-inset` |  | Present when `inset` indents the item to line up with items that have an icon. |
| `data-state` | `'open' \| 'closed'` | The state the part is in, one value per state. |

### ContextMenuTrigger

Marks the area where a right-click opens the context menu. Renders a `<span>`.

`data-slot="context-menu-trigger"` · `ContextMenuTrigger.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `disabled` | `boolean` | `false` | When `true`, the context menu would not open when right-clicking.<br><br>Note that this will also restore the native context menu. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'span'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-disabled` |  | Present while the part is disabled. |
| `data-state` | `'open' \| 'closed'` | The state the part is in, one value per state. |

## Styling

| Part | Recipe |
| --- | --- |
| `content` | [`context-menu-content`](https://ui.frontic.com/DESIGN.md#context-menu-content) |
| `sub-content` | [`context-menu-sub-content`](https://ui.frontic.com/DESIGN.md#context-menu-sub-content) |
| `item` | [`context-menu-item`](https://ui.frontic.com/DESIGN.md#context-menu-item) |
| `sub-trigger` | [`context-menu-sub-trigger`](https://ui.frontic.com/DESIGN.md#context-menu-sub-trigger) |
| `label` | [`context-menu-label`](https://ui.frontic.com/DESIGN.md#context-menu-label) |
| `separator` | [`context-menu-separator`](https://ui.frontic.com/DESIGN.md#context-menu-separator) |
| `shortcut` | [`context-menu-shortcut`](https://ui.frontic.com/DESIGN.md#context-menu-shortcut) |
| `disabled` | [`disabled`](https://ui.frontic.com/DESIGN.md#disabled) |

States with a look of their own: focus and disabled.

## Source

`components/ui/context-menu/ContextMenu.vue`

```vue
<script lang="ts">
/** Groups all parts of the context menu, which opens on right-click. */
export default {}
</script>

<script setup lang="ts">
import type { ContextMenuRootEmits, ContextMenuRootProps } from 'reka-ui'
import { ContextMenuRoot, useForwardPropsEmits } from 'reka-ui'

const props = defineProps<ContextMenuRootProps>()
const emits = defineEmits<ContextMenuRootEmits>()

const forwarded = useForwardPropsEmits(props, emits)
</script>

<template>
  <ContextMenuRoot data-slot="context-menu" v-bind="forwarded">
    <slot />
  </ContextMenuRoot>
</template>
```

`components/ui/context-menu/ContextMenuCheckboxItem.vue`

```vue
<script lang="ts">
/** Lets the user switch an option on or off from the menu. */
export default {}
</script>

<script setup lang="ts">
import { CheckIcon } from '@lucide/vue'

import type { ContextMenuCheckboxItemEmits, ContextMenuCheckboxItemProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { ContextMenuCheckboxItem, ContextMenuItemIndicator, useForwardPropsEmits } from 'reka-ui'

const props = defineProps<ContextMenuCheckboxItemProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<ContextMenuCheckboxItemEmits>()

defineSlots<{
  /** Replaces the check icon shown while the option is on. */
  'indicator-icon'?: () => unknown
  default?: () => unknown
}>()

const delegatedProps = reactiveOmit(props, 'class')

const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>

<template>
  <ContextMenuCheckboxItem
    data-slot="context-menu-checkbox-item"
    v-bind="forwarded"
    :class="[
      'fui-context-menu-item fui-context-menu-item-indicator relative cursor-default outline-hidden select-none data-disabled:pointer-events-none data-disabled:fui-disabled **:data-icon:pointer-events-none **:data-icon:shrink-0',
      props.class,
    ]"
  >
    <span class="pointer-events-none absolute inset-e-2">
      <ContextMenuItemIndicator>
        <slot name="indicator-icon">
          <CheckIcon data-icon />
        </slot>
      </ContextMenuItemIndicator>
    </span>
    <slot />
  </ContextMenuCheckboxItem>
</template>
```

`components/ui/context-menu/ContextMenuContent.vue`

```vue
<script lang="ts">
/** Holds the items the context menu shows while open. */
export default {}
</script>

<script setup lang="ts">
import type { ContextMenuContentEmits, ContextMenuContentProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { ContextMenuContent, ContextMenuPortal, useForwardPropsEmits } from 'reka-ui'

defineOptions({
  inheritAttrs: false,
})

const props = defineProps<ContextMenuContentProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<ContextMenuContentEmits>()

const delegatedProps = reactiveOmit(props, 'class')

const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>

<template>
  <ContextMenuPortal>
    <ContextMenuContent
      data-slot="context-menu-content"
      v-bind="{ ...$attrs, ...forwarded }"
      :class="[
        'fui-context-menu-content z-50 data-open:animate-in data-closed:animate-out data-open:fade-in-0 data-closed:fade-out-0 data-open:zoom-in-95 data-closed:zoom-out-95 data-side-bottom:slide-in-from-top-2 data-side-left:slide-in-from-right-2 data-side-right:slide-in-from-left-2 data-side-top:slide-in-from-bottom-2 duration-100 max-h-(--reka-context-menu-content-available-height) origin-(--reka-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto',
        props.class,
      ]"
    >
      <slot />
    </ContextMenuContent>
  </ContextMenuPortal>
</template>
```

`components/ui/context-menu/ContextMenuGroup.vue`

```vue
<script lang="ts">
/** Groups related items in the context menu. */
export default {}
</script>

<script setup lang="ts">
import type { ContextMenuGroupProps } from 'reka-ui'
import { ContextMenuGroup, useForwardProps } from 'reka-ui'

const props = defineProps<ContextMenuGroupProps>()
const forwarded = useForwardProps(props)
</script>

<template>
  <ContextMenuGroup data-slot="context-menu-group" v-bind="forwarded">
    <slot />
  </ContextMenuGroup>
</template>
```

`components/ui/context-menu/ContextMenuItem.vue`

```vue
<script lang="ts">
/** Holds one action the user can pick from the context menu. */
export default {}
</script>

<script setup lang="ts">
import type { ContextMenuItemEmits, ContextMenuItemProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { cva } from 'class-variance-authority'
import { reactiveOmit } from '@vueuse/core'
import { ContextMenuItem, useForwardPropsEmits } from 'reka-ui'

const props = withDefaults(
  defineProps<
    ContextMenuItemProps & {
      class?: HTMLAttributes['class']
      /** Indents the item to line up with items that show a check or icon. */
      inset?: boolean
      /** The item's intent: destructive marks an action that deletes or cannot be undone, such as removing an address. */
      color?: 'neutral' | 'destructive'
    }
  >(),
  {
    color: 'neutral',
  },
)
const emits = defineEmits<ContextMenuItemEmits>()

const delegatedProps = reactiveOmit(props, 'class', 'inset', 'color')

const forwarded = useForwardPropsEmits(delegatedProps, emits)

const item = cva(
  'fui-context-menu-item group/context-menu-item relative cursor-default outline-hidden select-none data-disabled:pointer-events-none data-disabled:fui-disabled **:data-icon:pointer-events-none **:data-icon:shrink-0',
  {
    variants: {
      color: { destructive: 'fui-context-menu-item-destructive' },
      pad: { default: 'fui-context-menu-item-default', inset: 'fui-context-menu-item-inset', indicator: 'fui-context-menu-item-indicator' },
    },
    defaultVariants: { pad: 'default' },
  },
)
</script>

<template>
  <ContextMenuItem
    data-slot="context-menu-item"
    :data-inset="inset ? '' : undefined"
    :data-color="color"
    v-bind="forwarded"
    :class="[item({ color: props.color === 'destructive' ? 'destructive' : undefined, pad: props.inset ? 'inset' : 'default' }), props.class]"
  >
    <slot />
  </ContextMenuItem>
</template>
```

`components/ui/context-menu/ContextMenuLabel.vue`

```vue
<script lang="ts">
/** Labels a group of items in the context menu. */
export default {}
</script>

<script setup lang="ts">
import type { ContextMenuLabelProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { cva } from 'class-variance-authority'
import { ContextMenuLabel, useForwardProps } from 'reka-ui'

const props = defineProps<
  ContextMenuLabelProps & {
    class?: HTMLAttributes['class']
    /** Indents the label to line up with items that show a check or icon. */
    inset?: boolean
  }
>()

const delegatedProps = reactiveOmit(props, 'class')

const label = cva('fui-context-menu-label', {
  variants: { pad: { default: 'fui-context-menu-label-default', inset: 'fui-context-menu-label-inset' } },
  defaultVariants: { pad: 'default' },
})
const forwarded = useForwardProps(delegatedProps)
</script>

<template>
  <ContextMenuLabel data-slot="context-menu-label" :data-inset="inset ? '' : undefined" v-bind="forwarded" :class="[label({ pad: props.inset ? 'inset' : 'default' }), props.class]">
    <slot />
  </ContextMenuLabel>
</template>
```

`components/ui/context-menu/ContextMenuPortal.vue`

```vue
<script lang="ts">
/** Places the menu content above the rest of the page. */
export default {}
</script>

<script setup lang="ts">
import type { ContextMenuPortalProps } from 'reka-ui'
import { ContextMenuPortal, useForwardProps } from 'reka-ui'

const props = defineProps<ContextMenuPortalProps>()
const forwarded = useForwardProps(props)
</script>

<template>
  <ContextMenuPortal data-slot="context-menu-portal" v-bind="forwarded">
    <slot />
  </ContextMenuPortal>
</template>
```

`components/ui/context-menu/ContextMenuRadioGroup.vue`

```vue
<script lang="ts">
/** Groups radio items so the user can pick exactly one. */
export default {}
</script>

<script setup lang="ts">
import type { ContextMenuRadioGroupEmits, ContextMenuRadioGroupProps } from 'reka-ui'
import { ContextMenuRadioGroup, useForwardPropsEmits } from 'reka-ui'

const props = defineProps<ContextMenuRadioGroupProps>()
const emits = defineEmits<ContextMenuRadioGroupEmits>()

const forwarded = useForwardPropsEmits(props, emits)
</script>

<template>
  <ContextMenuRadioGroup data-slot="context-menu-radio-group" v-bind="forwarded">
    <slot />
  </ContextMenuRadioGroup>
</template>
```

`components/ui/context-menu/ContextMenuRadioItem.vue`

```vue
<script lang="ts">
/** Lets the user choose one option from a group in the menu. */
export default {}
</script>

<script setup lang="ts">
import { CheckIcon } from '@lucide/vue'

import type { ContextMenuRadioItemEmits, ContextMenuRadioItemProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { ContextMenuItemIndicator, ContextMenuRadioItem, useForwardPropsEmits } from 'reka-ui'

const props = defineProps<ContextMenuRadioItemProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<ContextMenuRadioItemEmits>()

defineSlots<{
  /** Replaces the check icon shown on the chosen option. */
  'indicator-icon'?: () => unknown
  default?: () => unknown
}>()

const delegatedProps = reactiveOmit(props, 'class')

const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>

<template>
  <ContextMenuRadioItem
    data-slot="context-menu-radio-item"
    v-bind="forwarded"
    :class="[
      'fui-context-menu-item fui-context-menu-item-indicator relative cursor-default outline-hidden select-none data-disabled:pointer-events-none data-disabled:fui-disabled **:data-icon:pointer-events-none **:data-icon:shrink-0',
      props.class,
    ]"
  >
    <span class="pointer-events-none absolute inset-e-2">
      <ContextMenuItemIndicator>
        <slot name="indicator-icon">
          <CheckIcon data-icon />
        </slot>
      </ContextMenuItemIndicator>
    </span>
    <slot />
  </ContextMenuRadioItem>
</template>
```

`components/ui/context-menu/ContextMenuSeparator.vue`

```vue
<script lang="ts">
/** Separates groups of items in the context menu. */
export default {}
</script>

<script setup lang="ts">
import type { ContextMenuSeparatorProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { ContextMenuSeparator, useForwardProps } from 'reka-ui'

const props = defineProps<ContextMenuSeparatorProps & { class?: HTMLAttributes['class'] }>()

const delegatedProps = reactiveOmit(props, 'class')
const forwarded = useForwardProps(delegatedProps)
</script>

<template>
  <ContextMenuSeparator data-slot="context-menu-separator" v-bind="forwarded" :class="['fui-context-menu-separator', props.class]" />
</template>
```

`components/ui/context-menu/ContextMenuShortcut.vue`

```vue
<script lang="ts">
/** Shows the keyboard shortcut for a menu item. */
export default {}
</script>

<script setup lang="ts">
import type { PrimitiveProps } from 'reka-ui'
import { Primitive } from 'reka-ui'
import type { HTMLAttributes } from 'vue'

const props = withDefaults(
  defineProps<
    PrimitiveProps & {
      class?: HTMLAttributes['class']
    }
  >(),
  { as: 'span' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="context-menu-shortcut" :class="['fui-context-menu-shortcut', props.class]">
    <slot />
  </Primitive>
</template>
```

`components/ui/context-menu/ContextMenuSub.vue`

```vue
<script lang="ts">
/** Groups a submenu trigger with the submenu it opens. */
export default {}
</script>

<script setup lang="ts">
import type { ContextMenuSubEmits, ContextMenuSubProps } from 'reka-ui'
import { ContextMenuSub, useForwardPropsEmits } from 'reka-ui'

const props = defineProps<ContextMenuSubProps>()
const emits = defineEmits<ContextMenuSubEmits>()

const forwarded = useForwardPropsEmits(props, emits)
</script>

<template>
  <ContextMenuSub data-slot="context-menu-sub" v-bind="forwarded">
    <slot />
  </ContextMenuSub>
</template>
```

`components/ui/context-menu/ContextMenuSubContent.vue`

```vue
<script lang="ts">
/** Holds the items of a submenu while it is open. */
export default {}
</script>

<script setup lang="ts">
import type { DropdownMenuSubContentEmits, DropdownMenuSubContentProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { ContextMenuSubContent, useForwardPropsEmits } from 'reka-ui'

const props = defineProps<DropdownMenuSubContentProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<DropdownMenuSubContentEmits>()

const delegatedProps = reactiveOmit(props, 'class')

const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>

<template>
  <ContextMenuSubContent
    data-slot="context-menu-sub-content"
    v-bind="forwarded"
    :class="[
      'fui-context-menu-sub-content z-50 data-open:animate-in data-closed:animate-out data-open:fade-in-0 data-closed:fade-out-0 data-open:zoom-in-95 data-closed:zoom-out-95 data-side-bottom:slide-in-from-top-2 data-side-left:slide-in-from-right-2 data-side-right:slide-in-from-left-2 data-side-top:slide-in-from-bottom-2 duration-100 origin-(--reka-context-menu-content-transform-origin) overflow-hidden',
      props.class,
    ]"
  >
    <slot />
  </ContextMenuSubContent>
</template>
```

`components/ui/context-menu/ContextMenuSubTrigger.vue`

```vue
<script lang="ts">
/** Opens a submenu from an item of the context menu. */
export default {}
</script>

<script setup lang="ts">
import { ChevronRightIcon } from '@lucide/vue'

import type { ContextMenuSubTriggerProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { cva } from 'class-variance-authority'
import { reactiveOmit } from '@vueuse/core'
import { ContextMenuSubTrigger, useForwardProps } from 'reka-ui'

const props = defineProps<
  ContextMenuSubTriggerProps & {
    class?: HTMLAttributes['class']
    /** Indents the trigger to line up with items that show a check or icon. */
    inset?: boolean
  }
>()

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)

const subTrigger = cva('fui-context-menu-sub-trigger cursor-default outline-hidden select-none **:data-icon:pointer-events-none **:data-icon:shrink-0', {
  variants: { pad: { default: 'fui-context-menu-sub-trigger-default', inset: 'fui-context-menu-sub-trigger-inset' } },
  defaultVariants: { pad: 'default' },
})
</script>

<template>
  <ContextMenuSubTrigger
    data-slot="context-menu-sub-trigger"
    :data-inset="inset ? '' : undefined"
    v-bind="forwardedProps"
    :class="[subTrigger({ pad: props.inset ? 'inset' : 'default' }), props.class]"
  >
    <slot />
    <ChevronRightIcon class="ms-auto rtl:rotate-180" data-icon />
  </ContextMenuSubTrigger>
</template>
```

`components/ui/context-menu/ContextMenuTrigger.vue`

```vue
<script lang="ts">
/** Marks the area where a right-click opens the context menu. */
export default {}
</script>

<script setup lang="ts">
import type { ContextMenuTriggerProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { ContextMenuTrigger, useForwardProps } from 'reka-ui'

const props = defineProps<ContextMenuTriggerProps & { class?: HTMLAttributes['class'] }>()

const delegatedProps = reactiveOmit(props, 'class')
const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <ContextMenuTrigger data-slot="context-menu-trigger" v-bind="forwardedProps" :class="['select-none', props.class]">
    <slot />
  </ContextMenuTrigger>
</template>
```
