DropdownMenu
Usage
Use a Button or any other component in the default slot of the DropdownMenu.
Items
Use the items
prop as an array of objects with the following properties:
label?: string
icon?: string
avatar?: AvatarProps
kbds?: string[] | KbdProps[]
type?: "link" | "label" | "separator"
disabled?: boolean
class?: any
slot?: string
select?(e: Event): void
You can also pass any property from the Link component such as to
, target
, etc.
<script setup lang="ts">
const items = ref([
[
{
label: 'Benjamin',
avatar: {
src: 'https://github.com/benjamincanac.png'
},
type: 'label'
}
],
[
{
label: 'Profile',
icon: 'i-heroicons-user'
},
{
label: 'Billing',
icon: 'i-heroicons-credit-card'
},
{
label: 'Settings',
icon: 'i-heroicons-cog',
kbds: [',']
},
{
label: 'Keyboard shortcuts',
icon: 'i-heroicons-computer-desktop'
}
],
[
{
label: 'Team',
icon: 'i-heroicons-users'
},
{
label: 'Invite users',
icon: 'i-heroicons-user-plus',
children: [
[
{
label: 'Email',
icon: 'i-heroicons-envelope'
},
{
label: 'Message',
icon: 'i-heroicons-chat-bubble-left'
}
],
[
{
label: 'More',
icon: 'i-heroicons-plus-circle'
}
]
]
},
{
label: 'New team',
icon: 'i-heroicons-plus',
kbds: ['meta', 'n']
}
],
[
{
label: 'GitHub',
icon: 'i-simple-icons-github',
to: 'https://github.com/nuxt/ui',
target: '_blank'
},
{
label: 'Support',
icon: 'i-heroicons-lifebuoy',
to: '/components/dropdown-menu'
},
{
label: 'API',
icon: 'i-heroicons-cloud',
disabled: true
}
],
[
{
label: 'Logout',
icon: 'i-heroicons-arrow-right-on-rectangle',
kbds: ['shift', 'meta', 'q']
}
]
])
</script>
<template>
<UDropdownMenu :items="items" class="w-48">
<UButton icon="i-heroicons-bars-3" color="gray" variant="outline" />
</UDropdownMenu>
</template>
items
prop to create separated groups of items.children
array of objects with the same properties as the items
prop to create a nested menu which can be controlled using the open
, defaultOpen
and content
properties.Content
Use the content
prop to control how the DropdownMenu content is rendered, like its align
or side
for example.
<script setup lang="ts">
const items = ref([
{
label: 'Profile',
icon: 'i-heroicons-user'
},
{
label: 'Billing',
icon: 'i-heroicons-credit-card'
},
{
label: 'Settings',
icon: 'i-heroicons-cog'
}
])
</script>
<template>
<UDropdownMenu
:items="items"
:content="{
align: 'start',
side: 'bottom',
sideOffset: 8
}"
class="w-48"
>
<UButton
label="Open"
icon="i-heroicons-bars-3"
color="gray"
variant="outline"
/>
</UDropdownMenu>
</template>
Arrow
Use the arrow
prop to display an arrow on the DropdownMenu.
<script setup lang="ts">
const items = ref([
{
label: 'Profile',
icon: 'i-heroicons-user'
},
{
label: 'Billing',
icon: 'i-heroicons-credit-card'
},
{
label: 'Settings',
icon: 'i-heroicons-cog'
}
])
</script>
<template>
<UDropdownMenu arrow :items="items" class="w-48">
<UButton
label="Open"
icon="i-heroicons-bars-3"
color="gray"
variant="outline"
/>
</UDropdownMenu>
</template>
Size
Use the size
prop to control the size of the DropdownMenu.
<script setup lang="ts">
const items = ref([
{
label: 'Profile',
icon: 'i-heroicons-user'
},
{
label: 'Billing',
icon: 'i-heroicons-credit-card'
},
{
label: 'Settings',
icon: 'i-heroicons-cog'
}
])
</script>
<template>
<UDropdownMenu
size="xl"
:items="items"
:content="{
align: 'start'
}"
class="w-48"
>
<UButton
size="xl"
label="Open"
icon="i-heroicons-bars-3"
color="gray"
variant="outline"
/>
</UDropdownMenu>
</template>
size
prop will not be proxied to the Button, you need to set it yourself.Disabled
Use the disabled
prop to disable the DropdownMenu.
<script setup lang="ts">
const items = ref([
{
label: 'Profile',
icon: 'i-heroicons-user'
},
{
label: 'Billing',
icon: 'i-heroicons-credit-card'
},
{
label: 'Settings',
icon: 'i-heroicons-cog'
}
])
</script>
<template>
<UDropdownMenu disabled :items="items" class="w-48">
<UButton
label="Open"
icon="i-heroicons-bars-3"
color="gray"
variant="outline"
/>
</UDropdownMenu>
</template>
Examples
Control open state
You can control the open state by using the default-open
prop or the v-model:open
directive.
<script setup lang="ts">
const open = ref(false)
defineShortcuts({
o: () => open.value = !open.value
})
const items = [{
label: 'Profile',
icon: 'i-heroicons-user'
}, {
label: 'Billing',
icon: 'i-heroicons-credit-card'
}, {
label: 'Settings',
icon: 'i-heroicons-cog'
}]
</script>
<template>
<UDropdownMenu v-model:open="open" :items="items" class="w-48">
<UButton label="Open" color="gray" variant="outline" icon="i-heroicons-bars-3" />
</UDropdownMenu>
</template>
With custom slot
Use the slot
property to customize a specific item.
You will have access to the following slots:
#{{ item.slot }}
#{{ item.slot }}-leading
#{{ item.slot }}-label
#{{ item.slot }}-trailing
<script setup lang="ts">
const items = [{
label: 'Profile',
icon: 'i-heroicons-user',
slot: 'profile'
}, {
label: 'Billing',
icon: 'i-heroicons-credit-card'
}, {
label: 'Settings',
icon: 'i-heroicons-cog'
}]
</script>
<template>
<UDropdownMenu :items="items" class="w-48">
<UButton label="Open" color="gray" variant="outline" icon="i-heroicons-bars-3" />
<template #profile-trailing>
<UIcon name="i-heroicons-check-badge" class="shrink-0 size-5 text-primary-500 dark:text-primary-400" />
</template>
</UDropdownMenu>
</template>
#item
, #item-leading
, #item-label
and #item-trailing
slots to customize all items.Extract shortcuts
When you have some items with kbds
property (displaying some Kbd), you can easily make them work with the defineShortcuts composable.
Inside the defineShortcuts
composable, there is an extractShortcuts
utility that will extract the shortcuts recursively from the items and return an object that you can pass to defineShortcuts
. It will automatically call the select
function of the item when the shortcut is pressed.
<script setup lang="ts">
const items = [{
label: 'Invite users',
icon: 'i-heroicons-user-plus',
children: [{
label: 'Invite by email',
icon: 'i-heroicons-paper-airplane',
kbds: ['meta', 'e'],
select() {
console.log('Invite by email clicked')
}
}, {
label: 'Invite by link',
icon: 'i-heroicons-link',
kbds: ['meta', 'i'],
select() {
console.log('Invite by link clicked')
}
}]
}, {
label: 'New team',
icon: 'i-heroicons-plus',
kbds: ['meta', 'n'],
select() {
console.log('New team clicked')
}
}]
defineShortcuts(extractShortcuts(items))
</script>
select
function of the corresponding item.API
Props
Prop | Default | Type |
---|---|---|
size |
|
|
items |
| |
content |
|
The content of the menu. |
arrow |
|
Display an arrow alongside the menu. |
portal |
|
Render the menu in a portal. |
open |
The controlled open state of the menu. Can be used as | |
defaultOpen |
The open state of the dropdown menu when it is initially rendered. Use when you do not need to control its open state. | |
modal |
|
The modality of the dropdown menu. When set to |
disabled |
When | |
ui |
|
Slots
Slot | Type |
---|---|
default |
|
item |
|
item-leading |
|
item-label |
|
item-trailing |
|
Emits
Event | Type |
---|---|
update:open |
|
Theme
export default defineAppConfig({
ui: {
dropdownMenu: {
slots: {
content: 'min-w-32 bg-white dark:bg-gray-900 shadow-lg rounded-md ring ring-gray-200 dark:ring-gray-800 divide-y divide-gray-200 dark:divide-gray-800 overflow-y-auto scroll-py-1 data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in]',
arrow: 'fill-gray-200 dark:fill-gray-800',
group: 'p-1 isolate',
label: 'w-full flex items-center font-semibold text-gray-900 dark:text-white',
separator: '-mx-1 my-1 h-px bg-gray-200 dark:bg-gray-800',
item: 'group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75',
itemLeadingIcon: 'shrink-0',
itemLeadingAvatar: 'shrink-0',
itemLeadingAvatarSize: '',
itemTrailing: 'ms-auto inline-flex',
itemTrailingIcon: 'shrink-0',
itemTrailingKbds: 'hidden lg:inline-flex items-center shrink-0',
itemTrailingKbdsSize: '',
itemLabel: 'truncate',
itemLabelExternalIcon: 'size-3 align-top text-gray-400 dark:text-gray-500'
},
variants: {
active: {
true: {
item: 'text-gray-900 dark:text-white before:bg-gray-100 dark:before:bg-gray-800',
itemLeadingIcon: 'text-gray-700 dark:text-gray-200'
},
false: {
item: [
'text-gray-700 dark:text-gray-200 data-highlighted:text-gray-900 dark:data-highlighted:text-white data-[state=open]:text-gray-900 dark:data-[state=open]:text-white data-highlighted:before:bg-gray-50 dark:data-highlighted:before:bg-gray-800/50 data-[state=open]:before:bg-gray-50 dark:data-[state=open]:before:bg-gray-800/50',
'transition-colors before:transition-colors'
],
itemLeadingIcon: [
'text-gray-400 dark:text-gray-500 group-data-highlighted:text-gray-700 dark:group-data-highlighted:text-gray-200 group-data-[state=open]:text-gray-700 dark:group-data-[state=open]:text-gray-200',
'transition-colors'
]
}
},
size: {
xs: {
label: 'p-1 text-xs gap-1',
item: 'p-1 text-xs gap-1',
itemLeadingIcon: 'size-4',
itemLeadingAvatarSize: '3xs',
itemTrailingIcon: 'size-4',
itemTrailingKbds: 'gap-0.5',
itemTrailingKbdsSize: 'sm'
},
sm: {
label: 'p-1.5 text-xs gap-1.5',
item: 'p-1.5 text-xs gap-1.5',
itemLeadingIcon: 'size-4',
itemLeadingAvatarSize: '3xs',
itemTrailingIcon: 'size-4',
itemTrailingKbds: 'gap-0.5',
itemTrailingKbdsSize: 'sm'
},
md: {
label: 'p-1.5 text-sm gap-1.5',
item: 'p-1.5 text-sm gap-1.5',
itemLeadingIcon: 'size-5',
itemLeadingAvatarSize: '2xs',
itemTrailingIcon: 'size-5',
itemTrailingKbds: 'gap-0.5',
itemTrailingKbdsSize: 'md'
},
lg: {
label: 'p-2 text-sm gap-2',
item: 'p-2 text-sm gap-2',
itemLeadingIcon: 'size-5',
itemLeadingAvatarSize: '2xs',
itemTrailingIcon: 'size-5',
itemTrailingKbds: 'gap-1',
itemTrailingKbdsSize: 'md'
},
xl: {
label: 'p-2 text-base gap-2',
item: 'p-2 text-base gap-2',
itemLeadingIcon: 'size-6',
itemLeadingAvatarSize: 'xs',
itemTrailingIcon: 'size-6',
itemTrailingKbds: 'gap-1',
itemTrailingKbdsSize: 'lg'
}
}
},
defaultVariants: {
size: 'md'
}
}
}
})