Context Menu
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { ref } from 'vue'
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItem,
ContextMenuItemIndicator,
ContextMenuLabel,
ContextMenuPortal,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from 'radix-vue'
const checkboxOne = ref(false)
const checkboxTwo = ref(false)
const person = ref('pedro')
function handleClick() {
alert('hello!')
}
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger
as-child
class="block border-2 border-white border-dashed text-white rounded text-[15px] select-none py-[45px] w-[300px] text-center"
>
<span> Right click here. </span>
</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent
class="min-w-[220px] z-30 bg-white outline-none rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade"
:side-offset="5"
>
<ContextMenuItem
value="New Tab"
class="group text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
@click="handleClick"
>
New Tab <div
class="ml-auto pl-[20px] text-mauve11 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+T
</div>
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger
value="more toolsz"
class="group w-full text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:bg-green9 data-[highlighted]:data-[state=open]:text-green1"
>
More Tools <div
class="ml-auto pl-[20px] text-mauve11 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
<Icon icon="radix-icons:chevron-right" />
</div>
</ContextMenuSubTrigger>
<ContextMenuPortal>
<ContextMenuSubContent
class="min-w-[220px] z-30 outline-none bg-white rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade"
:side-offset="2" :align-offset="-5"
>
<ContextMenuItem
class="group text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Save Page As… <div
class="ml-auto pl-[20px] text-mauve11 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+S
</div>
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Create Shortcut…
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Name Window…
</ContextMenuItem>
<ContextMenuSeparator class="h-[1px] bg-green6 m-[5px]" />
<ContextMenuItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Developer Tools
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuPortal>
</ContextMenuSub>
<ContextMenuItem
value="New Window"
class="group text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
New Window <div
class="ml-auto pl-[20px] text-mauve11 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+N
</div>
</ContextMenuItem>
<ContextMenuItem
value="New Private Window"
class="group text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
disabled
>
New Private Window <div
class="ml-auto pl-[20px] text-mauve11 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⇧+⌘+N
</div>
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger
value="more tools"
class="group text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none w-full outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:bg-green9 data-[highlighted]:data-[state=open]:text-green1"
>
More Tools <div
class="ml-auto pl-[20px] text-mauve11 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
<Icon icon="radix-icons:chevron-right" />
</div>
</ContextMenuSubTrigger>
<ContextMenuPortal>
<ContextMenuSubContent
class="min-w-[220px] z-30 outline-none bg-white rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade"
:side-offset="2" :align-offset="-5"
>
<ContextMenuItem
class="group text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Save Page As… <div
class="ml-auto pl-[20px] text-mauve11 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+S
</div>
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Create Shortcut…
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Name Window…
</ContextMenuItem>
<ContextMenuSeparator class="h-[1px] bg-green6 m-[5px]" />
<ContextMenuItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Developer Tools
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger
value="more toolsz"
class="group w-full text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:bg-green9 data-[highlighted]:data-[state=open]:text-green1"
>
More Tools <div
class="ml-auto pl-[20px] text-mauve11 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
<Icon icon="radix-icons:chevron-right" />
</div>
</ContextMenuSubTrigger>
<ContextMenuPortal>
<ContextMenuSubContent
class="min-w-[220px] z-30 outline-none bg-white rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade"
:side-offset="2" :align-offset="-5"
>
<ContextMenuItem
class="group text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Save Page As… <div
class="ml-auto pl-[20px] text-mauve11 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+S
</div>
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Create Shortcut…
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Name Window…
</ContextMenuItem>
<ContextMenuSeparator class="h-[1px] bg-green6 m-[5px]" />
<ContextMenuItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Developer Tools
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger
value="more toolsz"
class="group w-full text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[state=open]:bg-green4 data-[state=open]:text-grass11 data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1 data-[highlighted]:data-[state=open]:bg-green9 data-[highlighted]:data-[state=open]:text-green1"
>
More Tools <div
class="ml-auto pl-[20px] text-mauve11 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
<Icon icon="radix-icons:chevron-right" />
</div>
</ContextMenuSubTrigger>
<ContextMenuPortal>
<ContextMenuSubContent
class="min-w-[220px] z-30 outline-none bg-white rounded-md p-[5px] shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade"
:side-offset="2" :align-offset="-5"
>
<ContextMenuItem
class="group text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Save Page As… <div
class="ml-auto pl-[20px] text-mauve11 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+S
</div>
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Create Shortcut…
</ContextMenuItem>
<ContextMenuItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Name Window…
</ContextMenuItem>
<ContextMenuSeparator class="h-[1px] bg-green6 m-[5px]" />
<ContextMenuItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Developer Tools
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuPortal>
</ContextMenuSub>
</ContextMenuSubContent>
</ContextMenuPortal>
</ContextMenuSub>
<ContextMenuItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
Developer Tools
</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuPortal>
</ContextMenuSub>
<ContextMenuSeparator class="h-[1px] bg-green6 m-[5px]" />
<ContextMenuCheckboxItem
v-model="checkboxOne"
class="group text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
<ContextMenuItemIndicator class="absolute left-0 w-[25px] inline-flex items-center justify-center">
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator> Show Bookmarks <div
class="ml-auto pl-[20px] text-mauve11 group-data-[highlighted]:text-white group-data-[disabled]:text-mauve8"
>
⌘+B
</div>
</ContextMenuCheckboxItem>
<ContextMenuCheckboxItem
v-model="checkboxTwo"
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
>
<ContextMenuItemIndicator class="absolute left-0 w-[25px] inline-flex items-center justify-center">
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator> Show Full URLs
</ContextMenuCheckboxItem>
<ContextMenuSeparator class="h-[1px] bg-green6 m-[5px]" />
<ContextMenuLabel class="pl-[25px] text-xs leading-[25px] text-mauve11">
People
</ContextMenuLabel>
<ContextMenuRadioGroup v-model="person">
<ContextMenuRadioItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
value="pedro"
>
<ContextMenuItemIndicator class="absolute left-0 w-[25px] inline-flex items-center justify-center">
<Icon icon="radix-icons:dot-filled" />
</ContextMenuItemIndicator> Pedro Duarte
</ContextMenuRadioItem>
<ContextMenuRadioItem
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] px-[5px] relative pl-[25px] select-none outline-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
value="colm"
>
<ContextMenuItemIndicator class="absolute left-0 w-[25px] inline-flex items-center justify-center">
<Icon icon="radix-icons:dot-filled" />
</ContextMenuItemIndicator> Colm Tuite
</ContextMenuRadioItem>
</ContextMenuRadioGroup>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
Features
- Supports submenus with configurable reading direction.
- Supports items, labels, groups of items.
- Supports checkable items (single or multiple) with optional indeterminate state.
- Supports modal and non-modal modes.
- Customize side, alignment, offsets, collision handling.
- Focus is fully managed.
- Full keyboard navigation.
- Typeahead support.
- Dismissing and layering behavior is highly customizable.
- Triggers with a long-press on touch devices
Installation
Install the component from your command line.
npm install radix-vue
Anatomy
Import all parts and piece them together.
<script setup lang="ts">
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuGroup,
ContextMenuItem,
ContextMenuItemIndicator,
ContextMenuLabel,
ContextMenuPortal,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from 'radix-vue'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger />
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuLabel />
<ContextMenuItem />
<ContextMenuGroup>
<ContextMenuItem />
</ContextMenuGroup>
<ContextMenuCheckboxItem>
<ContextMenuItemIndicator />
</ContextMenuCheckboxItem>
<ContextMenuRadioGroup>
<ContextMenuRadioItem>
<ContextMenuItemIndicator />
</ContextMenuRadioItem>
</ContextMenuRadioGroup>
<ContextMenuSub>
<ContextMenuSubTrigger />
<ContextMenuPortal>
<ContextMenuSubContent />
</ContextMenuPortal>
</ContextMenuSub>
<ContextMenuSeparator />
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
API Reference
Adheres to the Menu WAI-ARIA design pattern and uses roving tabindex to manage focus movement among menu items.
Root
Contains all the parts of a context menu.
Prop | Default | Type |
---|---|---|
dir | Direction The reading direction of the combobox when applicable. If omitted, inherits globally from | |
modal | true | boolean The modality of the dropdown menu. When set to |
Emit | Type |
---|---|
update:open | [payload: boolean] |
Trigger
The area that opens the context menu. Wrap it around the target you want the context menu to open from when right-clicking (or using the relevant keyboard shortcuts).
Prop | Default | Type |
---|---|---|
as | 'span' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Setting | |
disabled | false | boolean When Note that this will also restore the native context menu. |
Data Attribute | Value |
---|---|
[data-state] | "open" | "closed" |
Portal
When used, portals the content part into the body
.
Prop | Default | Type |
---|---|---|
disabled | boolean Disable teleport and render the component inline {@link https://vuejs.org/guide/built-ins/teleport.html#disabling-teleport} | |
forceMount | boolean Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | |
to | string | HTMLElement Vue native teleport component props. (to) {@link https://vuejs.org/guide/built-ins/teleport.html#basic-usage} |
Content
The component that pops out in an open context menu.
Prop | Default | Type |
---|---|---|
alignOffset | 0 | number An offset in pixels from the "start" or "end" alignment options. |
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Setting | |
avoidCollisions | true | boolean When true, overrides the side andalign preferences to prevent collisions with boundary edges. |
collisionBoundary | [] | Element | (Element | null)[] | 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 | 0 | number | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>> 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 }. |
forceMount | boolean Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | |
hideWhenDetached | false | boolean Whether to hide the content when the trigger becomes fully occluded. |
loop | boolean When | |
sticky | 'partial' | 'partial' | 'always' 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. |
Emit | Type |
---|---|
closeAutoFocus | [event: Event] |
dismiss | [] |
entryFocus | [event: Event] |
escapeKeyDown | [event: KeyboardEvent] |
focusOutside | [event: FocusOutsideEvent] |
interactOutside | [event: PointerDownOutsideEvent | FocusOutsideEvent] |
openAutoFocus | [event: Event] |
pointerDownOutside | [event: PointerDownOutsideEvent] |
Data Attribute | Value |
---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
CSS Variable | Description |
---|---|
--radix-context-menu-content-transform-origin |
The transform-origin computed from the content and arrow positions/offsets
|
--radix-context-menu-content-available-width |
The remaining width between the trigger and the boundary edge
|
--radix-context-menu-content-available-height |
The remaining height between the trigger and the boundary edge
|
--radix-context-menu-trigger-width | The width of the trigger |
--radix-context-menu-trigger-height | The height of the trigger |
Arrow
An optional arrow element to render alongside a submenu. This can be used to help visually link the trigger item with the ContextMenu.Content
. Must be rendered inside ContextMenu.Content
.
Prop | Default | Type |
---|---|---|
as | 'svg' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Setting | |
height | 5 | number The height of the arrow in pixels. |
width | 10 | number The width of the arrow in pixels. |
Item
The component that contains the context menu items.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Setting | |
disabled | boolean When | |
textValue | string Optional text used for typeahead purposes. By default the typeahead behavior will use the |
Emit | Type |
---|---|
select | [event: Event] |
Data Attribute | Value |
---|---|
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
Group
Used to group multiple ContextMenu.Item
s.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Setting |
Label
Used to render a label. It won't be focusable using arrow keys.
Prop | Default | Type |
---|---|---|
as | div | string | Component The element or component this component should render as. Can be overwrite by |
asChild | false | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. |
CheckboxItem
An item that can be controlled and rendered like a checkbox.
Prop | Default | Type |
---|---|---|
as | div | string | Component The element or component this component should render as. Can be overwrite by |
asChild | false | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. |
checked | boolean | 'indeterminate'
The controlled checked state of the item. Must be used in conjunction
with | |
disabled | boolean
When | |
textValue | string
Optional text used for typeahead purposes. By default the typeahead
behavior will use the |
Emit | Type |
---|---|
@update:checked | (checked: boolean) => void |
@select | (event: Event) => void |
Data Attribute | Value |
---|---|
[data-state] | "checked" | "unchecked" | "indeterminate" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
RadioGroup
Used to group multiple ContextMenu.RadioItem
s.
Prop | Default | Type |
---|---|---|
as | div | string | Component The element or component this component should render as. Can be overwrite by |
asChild | false | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. |
modelValue | string The value of the selected item in the group. |
Emit | Type |
---|---|
@update:modelValue | (value: string) => void |
RadioItem
An item that can be controlled and rendered like a radio.
Prop | Default | Type |
---|---|---|
as | div | string | Component The element or component this component should render as. Can be overwrite by |
asChild | false | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. |
value* | string The unique value of the item. | |
disabled | boolean
When | |
textValue | string
Optional text used for typeahead purposes. By default the typeahead
behavior will use the |
Emit | Type |
---|---|
@select | (event: Event) => void |
Data Attribute | Value |
---|---|
[data-state] | "checked" | "unchecked" | "indeterminate" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
ItemIndicator
Renders when the parent ContextMenu.CheckboxItem
or ContextMenu.RadioItem
is checked. You can style this element directly, or you can use it as a wrapper to put an icon into, or both.
Prop | Default | Type |
---|---|---|
as | span | string | Component The element or component this component should render as. Can be overwrite by |
asChild | false | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. |
forceMount | boolean Used to force mounting when more control is needed. Useful when controlling animation with Vue.js animation libraries. |
Data Attribute | Value |
---|---|
[data-state] | "checked" | "unchecked" | "indeterminate" |
Separator
Used to visually separate items in the context menu.
Prop | Default | Type |
---|---|---|
as | div | string | Component The element or component this component should render as. Can be overwrite by |
asChild | false | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. |
Sub
Contains all the parts of a submenu.
Prop | Default | Type |
---|---|---|
defaultOpen | boolean 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 submenu. Must be used in conjunction with |
Emit | Type |
---|---|
@update:open | (open: boolean) => void |
SubTrigger
An item that opens a submenu. Must be rendered inside ContextMenu.Sub
.
Prop | Default | Type |
---|---|---|
as | div | string | Component The element or component this component should render as. Can be overwrite by |
asChild | false | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. |
disabled | boolean
When | |
textValue | string
Optional text used for typeahead purposes. By default the typeahead behavior will use the |
Data Attribute | Value |
---|---|
[data-state] | "open" | "closed" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
SubContent
The component that pops out when a submenu is open. Must be rendered inside ContextMenu.Sub
.
Prop | Default | Type |
---|---|---|
loop | false | boolean
When |
forceMount | boolean
Used to force mounting when more control is needed. Useful when controlling animation with Vue.js animation libraries. It inherits from | |
sideOffset | 0 | number The distance in pixels from the trigger |
alignOffset | 0 | number
An offset in pixels from the |
avoidCollisions | true | boolean
When |
collisionBoundary | [] | Boundary 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 | 0 | number | Padding
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: |
arrowPadding | 0 | number
The padding between the arrow and the edges of the content. If your content has |
sticky | "partial" | enum
The sticky behavior on the align axis. |
hideWhenDetached | false | boolean Whether to hide the content when the trigger becomes fully occluded. |
as | div | string | Component The element or component this component should render as. Can be overwrite by |
asChild | false | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. |
Emit | Type |
---|---|
@escapeKeyDown | (event: KeyboardEvent) => void |
@pointerDownOutside | (event: PointerDownOutsideEvent) => void |
@focusOutside | (event: FocusOutsideEvent) => void |
@interactOutside | (event: FocusEvent | MouseEvent | TouchEvent) => void |
Data Attribute | Value |
---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
CSS Variable | Description |
---|---|
--radix-context-menu-content-transform-origin |
The transform-origin computed from the content and arrow positions/offsets
|
--radix-context-menu-content-available-width | The remaining width between the trigger and the boundary edge |
--radix-context-menu-content-available-height | The remaining height between the trigger and the boundary edge |
--radix-context-menu-trigger-width | The width of the trigger |
--radix-context-menu-trigger-height | The height of the trigger |
Examples
With submenus
You can create submenus by using ContextMenuSub
in combination with its parts.
<script setup lang="ts">
import {
ContextMenuContent,
ContextMenuItem,
ContextMenuLabel,
ContextMenuPortal,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from 'radix-vue'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuSub>
<ContextMenuSubTrigger>Sub menu →</ContextMenuSubTrigger>
<ContextMenuPortal>
<ContextMenuSubContent>
<ContextMenuItem>Sub menu item</ContextMenuItem>
<ContextMenuItem>Sub menu item</ContextMenuItem>
<ContextMenuArrow />
</ContextMenuSubContent>
</ContextMenuPortal>
</ContextMenuSub>
<ContextMenuSeparator />
<ContextMenuItem>…</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
With disabled items
You can add special styles to disabled items via the data-disabled
attribute.
<script setup lang="ts">
import { ContextMenuContent, ContextMenuItem, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from 'radix-vue'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem class="ContextMenuItem" disabled>
…
</ContextMenuItem>
<ContextMenuItem class="ContextMenuItem">
…
</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
/* styles.css */
.ContextMenuItem[data-disabled] {
color: gainsboro;
}
With separators
Use the Separator
part to add a separator between items.
<script setup lang="ts">
import {
ContextMenuContent,
ContextMenuItem,
ContextMenuPortal,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuTrigger,
} from 'radix-vue'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem>…</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
With labels
Use the Label
part to help label a section.
<script setup lang="ts">
import {
ContextMenuContent,
ContextMenuItem,
ContextMenuLabel,
ContextMenuPortal,
ContextMenuRoot,
ContextMenuTrigger,
} from 'radix-vue'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuLabel>Label</ContextMenuLabel>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuItem>…</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
With checkbox items
Use the CheckboxItem
part to add an item that can be checked.
<script setup lang="ts">
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItem,
ContextMenuItemIndicator,
ContextMenuPortal,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuTrigger,
} from 'radix-vue'
import { Icon } from '@iconify/vue'
const checked = ref(true)
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuCheckboxItem v-model="checked">
<ContextMenuItemIndicator>
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator>
Checkbox item
</ContextMenuCheckboxItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
With radio items
Use the RadioGroup
and RadioItem
parts to add an item that can be checked amongst others.
<script setup lang="ts">
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItem,
ContextMenuItemIndicator,
ContextMenuPortal,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuTrigger,
} from 'radix-vue'
import { Icon } from '@iconify/vue'
const color = ref('blue')
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuRadioGroup v-model="color">
<ContextMenuRadioItem value="red">
<ContextMenuItemIndicator>
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator>
Red
</ContextMenuRadioItem>
<ContextMenuRadioItem value="blue">
<ContextMenuItemIndicator>
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator>
Blue
</ContextMenuRadioItem>
<ContextMenuRadioItem value="green">
<ContextMenuItemIndicator>
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator>
Green
</ContextMenuRadioItem>
</ContextMenuRadioGroup>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
With complex items
You can add extra decorative elements in the Item
parts, such as images.
<script setup lang="ts">
import { ContextMenuContent, ContextMenuItem, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from 'radix-vue'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem>
<img src="…">
Adolfo Hess
</ContextMenuItem>
<ContextMenuItem>
<img src="…">
Miyah Myles
</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
Constrain the content/sub-content size
You may want to constrain the width of the content (or sub-content) so that it matches the trigger (or sub-trigger) width. You may also want to constrain its height to not exceed the viewport.
We expose several CSS custom properties such as --radix-context-menu-trigger-width
and --radix-context-menu-content-available-height
to support this. Use them to constrain the content dimensions.
<script setup lang="ts">
import { ContextMenuContent, ContextMenuItem, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from 'radix-vue'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent class="ContextMenuContent">
…
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
/* styles.css */
.ContextMenuContent {
width: var(--radix-context-menu-trigger-width);
max-height: var(--radix-context-menu-content-available-height);
}
Origin-aware animations
We expose a CSS custom property --radix-context-menu-content-transform-origin
. Use it to animate the content from its computed origin based on side
, sideOffset
, align
, alignOffset
and any collisions.
<script setup lang="ts">
import { ContextMenuContent, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from 'radix-vue'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent class="ContextMenuContent">
…
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
/* styles.css */
.ContextMenuContent {
transform-origin: var(--radix-context-menu-content-transform-origin);
animation: scaleIn 0.5s ease-out;
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0);
}
to {
opacity: 1;
transform: scale(1);
}
}
Collision-aware animations
We expose data-side
and data-align
attributes. Their values will change at runtime to reflect collisions. Use them to create collision and direction-aware animations.
<script setup lang="ts">
import { ContextMenuContent, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from 'radix-vue'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent class="ContextMenuContent">
…
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>
/* styles.css */
.ContextMenuContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.ContextMenuContent[data-side="top"] {
animation-name: slideUp;
}
.ContextMenuContent[data-side="bottom"] {
animation-name: slideDown;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Accessibility
Uses roving tabindex to manage focus movement among menu items.
Keyboard Interactions
Key | Description |
---|---|
Space | Activates the focused item. |
Enter | Activates the focused item. |
ArrowDown | Moves focus to the next item. |
ArrowUp | Moves focus to the previous item. |
ArrowRightArrowLeft |
When focus is on ContextMenu.SubTrigger , opens or closes
the submenu depending on reading direction.
|
Esc | Closes the context menu |