Dropdown Menu
Displays a menu to the user — such as a set of actions or functions — triggered by a button.
Examples
Basic
Prop | Default | Type | Description |
---|---|---|---|
items | [] | DropdownMenuItemProps[] | The items to display in the dropdown-menu. |
label | - | string | The label to display in the dropdown-menu. |
defaultOpen | false | boolean | The open state of the dropdown menu when it is initially rendered. Use when you do not need to control its open state. |
dir | ltr | ltr , rtl | The reading direction of the combobox when applicable. If omitted, inherits globally from ConfigProvider or assumes LTR (left-to-right) reading mode. |
modal | true | boolean | The modality of the dropdown menu. When set to true, interaction with outside elements will be disabled and only menu content will be visible to screen readers. |
open | false | boolean | The controlled open state of the menu. Can be used as v-model:open . |
Preview
Code
Read more in Radix Dropdown Menu Root API.
Inset
Prop | Default | Type | Description |
---|---|---|---|
inset | false | boolean | Set the dropdown-menu to be inset. |
Preview
Code
Variant and Color
Prop | Default | Type | Description |
---|---|---|---|
dropdown-menu | solid-white | {variant}-{color} | Change the color of the dropdown-menu. |
dropdown-menu-item | gray | {color} | Change the color of the dropdown-menu item. |
_dropdown-menu-trigger.dropdown-menu | solid-white | {variant}-{color} | Change the color of the dropdown-menu trigger. |
_dropdown-menu-item.dropdown-menu-item | gray | {color} | Change the color of the dropdown-menu item. |
Preview
Code
Read more in Button variant and color section
Size
Adjust the dropdown-menu size without limits. Use breakpoints
(e.g., sm:sm
, xs:lg
) for responsive sizes or states
(e.g., hover:lg
, focus:3xl
) for state-based sizes.
Prop | Default | Type | Description |
---|---|---|---|
size | sm | string | Adjusts the overall size of the dropdown-menu component. |
_dropdownMenuItem.size | sm | string | Customizes the size of each item within the dropdown-menu dropdown. |
_dropdownMenuTrigger.size | sm | string | Modifies the size of the dropdown-menu trigger element. |
_dropdownMenuLabel.size | sm | string | Adjusts the size of the dropdown-menu label. |
Preview
Code
Slots
Name | Props | Description |
---|---|---|
trigger | - | The trigger slot. |
item | item | The item slot. |
sub-trigger | - | The sub-trigger slot. |
content | items | The content slot. |
label | label | The label slot. |
group | items | The group slot. |
Preview
Code
Presets
shortcuts/dropdown-menu.ts
type DropdownMenuPrefix = 'dropdown-menu'
export const staticDropdownMenu: Record<`${DropdownMenuPrefix}-${string}` | DropdownMenuPrefix, string> = {
// configurations
'dropdown-menu': '',
'dropdown-menu-default-variant': 'btn-solid-white',
// dropdown-menu-trigger
'dropdown-menu-trigger': '',
'dropdown-menu-trigger-leading': '',
'dropdown-menu-trigger-trailing': 'ml-auto',
// dropdown-menu-content
'dropdown-menu-content': 'z-50 min-w-32 overflow-hidden rounded-md border border-base bg-popover p-1 text-popover shadow-md',
// dropdown-menu-item
'dropdown-menu-item-base': 'text-left transition-color focus-visible:outline-0',
'dropdown-menu-item-leading': 'text-1em',
'dropdown-menu-item-trailing': 'ml-auto opacity-75 text-1em',
// dropdown-menu-label
'dropdown-menu-label': 'px-2 py-1.5 text-1em font-semibold',
// dropdown-menu-separator
'dropdown-menu-separator-root': 'relative -mx-1',
'dropdown-menu-separator': '',
// dropdown-menu-shortcut
'dropdown-menu-shortcut': 'pl-10 ml-auto text-0.875em tracking-widest n-disabled space-x-0.5',
// dropdown-menu-group
'dropdown-menu-group': '',
// dropdown-menu-sub
'dropdown-menu-sub-trigger': 'transition-color focus-visible:outline-0',
'dropdown-menu-sub-trigger-leading': 'text-1em',
'dropdown-menu-sub-trigger-trailing': 'ml-auto opacity-75 text-1em',
'dropdown-menu-sub-trigger-trailing-icon': 'i-lucide-chevron-right',
'dropdown-menu-sub-content': 'z-50 min-w-32 overflow-hidden rounded-md border border-base bg-popover p-1 text-popover shadow-lg',
}
export const dynamicDropdownMenu = [
[/^dropdown-menu-([^-]+)-([^-]+)$/, ([, v = 'solid', c = 'white']) => `btn-${v}-${c}`],
[/^dropdown-menu-item(?:-(\S+))?$/, ([, c = 'gray']) => `focus:bg-${c}-100 focus:text-${c}-800 dark:focus:bg-${c}-800 dark:focus:text-${c}-100 data-[state=open]:bg-${c}-100 dark:data-[state=open]:bg-${c}-800`],
]
export const dropdowMenu = [
...dynamicDropdownMenu,
staticDropdownMenu,
]
Props
types/dropdown-menu.ts
import type {
DropdownMenuContentProps,
DropdownMenuGroupProps,
DropdownMenuLabelProps,
DropdownMenuRootProps,
DropdownMenuSeparatorProps,
DropdownMenuSubContentProps,
DropdownMenuSubTriggerProps,
DropdownMenuTriggerProps,
} from 'radix-vue'
import type { HTMLAttributes } from 'vue'
import type { NButtonProps } from './button'
import type { NSeparatorProps } from './separator'
/**
* Base extensions for dropdown menu components.
*/
interface BaseExtensions {
/** CSS class for the component */
class?: HTMLAttributes['class']
/** Size of the component */
size?: HTMLAttributes['class']
}
/**
* Props for the NDropdownMenu component.
*/
export interface NDropdownMenuProps extends
Omit<NDropdownMenuRootProps, 'class' | 'size'>,
Omit<NDropdownMenuTriggerProps, 'una'>,
Pick<NDropdownMenuItemProps, 'shortcut' | 'dropdownMenuItem'> {
/** Label for the menu */
menuLabel?: string
/** Items in the dropdown menu */
items?: NDropdownMenuProps[]
/** Whether the menu is inset */
inset?: boolean
// Subcomponents
/** Props for the dropdown menu root */
_dropdownMenuRoot?: Partial<NDropdownMenuRootProps>
/** Props for the dropdown menu item */
_dropdownMenuItem?: Partial<NDropdownMenuItemProps>
/** Props for the dropdown menu trigger */
_dropdownMenuTrigger?: Partial<NDropdownMenuTriggerProps>
/** Props for the dropdown menu content */
_dropdownMenuContent?: Partial<NDropdownMenuContentProps>
/** Props for the dropdown menu sub-content */
_dropdownMenuSubContent?: Partial<NDropdownMenuSubContentProps>
/** Props for the dropdown menu label */
_dropdownMenuLabel?: Partial<NDropdownMenuLabelProps>
/** Props for the dropdown menu separator */
_dropdownMenuSeparator?: Partial<NDropdownMenuSeparatorProps>
/** Props for the dropdown menu group */
_dropdownMenuGroup?: Partial<NDropdownMenuGroupProps>
/** Props for the dropdown menu sub-trigger */
_dropdownMenuSubTrigger?: Partial<NDropdownMenuSubTriggerProps>
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps & NButtonProps['una']
}
/**
* Props for the NDropdownMenuRoot component.
*/
export interface NDropdownMenuRootProps extends BaseExtensions, DropdownMenuRootProps {
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuRoot']
}
/**
* Props for the NDropdownMenuTrigger component.
*/
export interface NDropdownMenuTriggerProps extends NButtonProps, DropdownMenuTriggerProps {
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuTrigger'] & NButtonProps['una']
}
/**
* Props for the NDropdownMenuContent component.
*/
export interface NDropdownMenuContentProps extends BaseExtensions, DropdownMenuContentProps {
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuContent']
}
/**
* Props for the NDropdownMenuLabel component.
*/
export interface NDropdownMenuLabelProps extends BaseExtensions, DropdownMenuLabelProps {
/** Whether the label is inset */
inset?: boolean
/** Size of the label */
size?: HTMLAttributes['class']
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuLabel']
}
/**
* Props for the NDropdownMenuSeparator component.
*/
export interface NDropdownMenuSeparatorProps extends DropdownMenuSeparatorProps, NSeparatorProps {
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuSeparator'] & NSeparatorProps['una']
}
/**
* Props for the NDropdownMenuGroup component.
*/
export interface NDropdownMenuGroupProps extends BaseExtensions, DropdownMenuGroupProps {
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuGroup']
}
/**
* Props for the NDropdownMenuSubContent component.
*/
export interface NDropdownMenuSubContentProps extends BaseExtensions, DropdownMenuSubContentProps {
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuSubContent']
}
/**
* Props for the NDropdownMenuItem component.
*/
export interface NDropdownMenuItemProps extends NButtonProps {
/** Dropdown menu item */
dropdownMenuItem?: HTMLAttributes['class']
/** Whether the item is inset */
inset?: boolean
/** Shortcut key for the item */
shortcut?: string
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuItem'] & NButtonProps['una']
}
/**
* Props for the NDropdownMenuSubTrigger component.
*/
export interface NDropdownMenuSubTriggerProps extends NButtonProps, DropdownMenuSubTriggerProps {
/** Dropdown menu item */
dropdownMenuItem?: HTMLAttributes['class']
/** Whether the sub-trigger is inset */
inset?: boolean
}
/**
* Props for the NDropdownMenuShortcut component.
*/
export interface NDropdownMenuShortcutProps extends BaseExtensions {
/** Shortcut key for the item */
value?: string
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuShortcut']
}
/**
* Props for the NDropdownMenuUna component.
*/
interface NDropdownMenuUnaProps {
/** CSS class for the dropdown menu content */
dropdownMenuContent?: HTMLAttributes['class']
/** CSS class for the dropdown menu sub-content */
dropdownMenuSubContent?: HTMLAttributes['class']
/** CSS class for the dropdown menu sub-trigger */
dropdownMenuSubTrigger?: HTMLAttributes['class']
/** CSS class for the dropdown menu trigger */
dropdownMenuTrigger?: HTMLAttributes['class']
/** CSS class for the dropdown menu label */
dropdownMenuLabel?: HTMLAttributes['class']
/** CSS class for the dropdown menu separator */
dropdownMenuSeparator?: HTMLAttributes['class']
/** CSS class for the dropdown menu group */
dropdownMenuGroup?: HTMLAttributes['class']
/** CSS class for the dropdown menu item */
dropdownMenuItem?: HTMLAttributes['class']
/** CSS class for the dropdown menu root */
dropdownMenuRoot?: HTMLAttributes['class']
/** CSS class for the dropdown menu shortcut */
dropdownMenuShortcut?: HTMLAttributes['class']
}
Components
DropdownMenu.vue
DropdownMenuTrigger.vue
DropdownMenuItem.vue
DropdownMenuGroup.vue
DropdownMenuLabel.vue
DropdownMenuSeparator.vue
DropdownMenuContent.vue
DropdownMenuSub.vue
DropdownMenuSubTrigger
DropdownMenuSubContent
<script setup lang="ts">
import type { DropdownMenuContentEmits, DropdownMenuRootEmits } from 'radix-vue'
import type { NDropdownMenuProps } from '../../../types'
import { createReusableTemplate, reactivePick } from '@vueuse/core'
import { DropdownMenuPortal, useForwardPropsEmits } from 'radix-vue'
import { omitProps } from '../../../utils'
import DropdownMenuContent from './DropdownMenuContent.vue'
import DropdownMenuGroup from './DropdownMenuGroup.vue'
import DropdownMenuItem from './DropdownMenuItem.vue'
import DropdownMenuLabel from './DropdownMenuLabel.vue'
import DropdownMenuRoot from './DropdownMenuRoot.vue'
import DropdownMenuSeparator from './DropdownMenuSeparator.vue'
import DropdownMenuSub from './DropdownMenuSub.vue'
import DropdownMenuSubContent from './DropdownMenuSubContent.vue'
import DropdownMenuSubTrigger from './DropdownMenuSubTrigger.vue'
import DropdownMenuTrigger from './DropdownMenuTrigger.vue'
const props = defineProps<NDropdownMenuProps>()
const emits = defineEmits<DropdownMenuRootEmits & DropdownMenuContentEmits>()
const forwarded = useForwardPropsEmits(props, emits)
const [DefineMenuSub, ReuseMenuSub] = createReusableTemplate<NDropdownMenuProps>()
</script>
<template>
<DropdownMenuRoot
v-bind="reactivePick(forwarded, ['defaultOpen', 'open', 'modal', 'dir'])"
>
<DropdownMenuTrigger
v-bind="omitProps({ ...forwarded, ...forwarded._dropdownMenuTrigger }, [
'dropdownMenuItem',
'items',
'menuLabel',
'_dropdownMenuItem',
'_dropdownMenuContent',
'_dropdownMenuLabel',
'_dropdownMenuSeparator',
'_dropdownMenuGroup',
'_dropdownMenuTrigger',
'_dropdownMenuSubTrigger',
'_dropdownMenuSubContent',
])"
>
<slot />
</DropdownMenuTrigger>
<DropdownMenuContent
v-bind="forwarded._dropdownMenuContent"
>
<slot name="content">
<template
v-if="menuLabel || $slots['menu-label']"
>
<DropdownMenuLabel
:size
:inset
:una="forwarded.una?.dropdownMenuLabel"
v-bind="forwarded._dropdownMenuLabel"
>
<slot name="menu-label">
{{ menuLabel }}
</slot>
</DropdownMenuLabel>
<DropdownMenuSeparator
:una="forwarded.una?.dropdownMenuSeparator"
v-bind="forwarded._dropdownMenuSeparator"
/>
</template>
<slot name="items" :items>
<DropdownMenuGroup
:una="forwarded.una?.dropdownMenuGroup"
v-bind="forwarded._dropdownMenuGroup"
>
<template
v-for="item in items"
:key="item.label"
>
<slot
v-if="!item.items && item.label"
:name="`item-${item.label}`"
>
<DropdownMenuItem
:size
:inset
:dropdown-menu-item
:una="forwarded.una?.dropdownMenuItem"
v-bind="{ ...item, ...forwarded._dropdownMenuItem, ...item._dropdownMenuItem }"
/>
</slot>
<DropdownMenuSeparator
v-else-if="!item.label && !item.items"
:una="forwarded.una?.dropdownMenuSeparator"
v-bind="{ ...forwarded._dropdownMenuSeparator, ...item._dropdownMenuSeparator }"
/>
<ReuseMenuSub
v-else
v-bind="item"
/>
</template>
</DropdownMenuGroup>
</slot>
</slot>
</DropdownMenuContent>
</DropdownMenuRoot>
<DefineMenuSub
v-slot="subProps"
as="div"
>
<template
v-if="subProps.menuLabel"
>
<DropdownMenuLabel
:size
:inset
:una="forwarded.una?.dropdownMenuLabel"
v-bind="{ ...forwarded._dropdownMenuLabel, ...subProps._dropdownMenuLabel }"
>
{{ subProps.menuLabel }}
</DropdownMenuLabel>
<DropdownMenuSeparator
:una="forwarded.una?.dropdownMenuSeparator"
v-bind="{ ...forwarded._dropdownMenuSeparator, ...subProps._dropdownMenuSeparator }"
/>
</template>
<DropdownMenuGroup
:una="forwarded.una?.dropdownMenuGroup"
v-bind="{ ...forwarded._dropdownMenuGroup, ...subProps._dropdownMenuGroup }"
>
<DropdownMenuSub>
<DropdownMenuSubTrigger
:size
:inset
:una="forwarded.una?.dropdownMenuSubTrigger"
:dropdown-menu-item
v-bind="omitProps({
...subProps,
...forwarded._dropdownMenuSubTrigger,
...subProps._dropdownMenuSubTrigger,
}, ['$slots'])"
>
<slot name="sub-trigger" :label="subProps.label" />
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent
v-bind="subProps._dropdownMenuSubContent"
:una="forwarded.una?.dropdownMenuSubContent"
>
<template
v-for="subItem in subProps.items"
:key="subItem.label"
>
<DropdownMenuItem
v-if="!subItem.items && subItem.label"
:size
:inset
:dropdown-menu-item
:una="forwarded.una?.dropdownMenuItem"
v-bind="{ ...subItem, ...forwarded._dropdownMenuItem, ...subItem._dropdownMenuItem }"
>
{{ subItem.label }}
</DropdownMenuItem>
<DropdownMenuSeparator
v-else-if="!subItem.label && !subItem.items"
:una="forwarded.una?.dropdownMenuSeparator"
v-bind="{ ...forwarded._dropdownMenuSeparator, ...subItem._dropdownMenuSeparator }"
/>
<ReuseMenuSub
v-else
v-bind="subItem"
/>
</template>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
</DropdownMenuGroup>
</DefineMenuSub>
</template>