Use a Button or any other component in the default slot of the DropdownMenu.
<script setup lang="ts">
const items = ref([
[
{
label: 'Benjamin',
avatar: {
src: 'https://github.com/praburangki.png'
},
type: 'label'
}
],
[
{
label: 'Profile',
icon: 'i-lucide:user'
},
{
label: 'Billing',
icon: 'i-lucide:credit-card'
},
{
label: 'Settings',
icon: 'i-lucide:cog',
kbds: [',']
},
{
label: 'Keyboard shortcuts',
icon: 'i-lucide:monitor'
}
],
[
{
label: 'Team',
icon: 'i-lucide:users'
},
{
label: 'Invite users',
icon: 'i-lucide:user-plus',
children: [
[
{
label: 'Email',
icon: 'i-lucide:mail'
},
{
label: 'Message',
icon: 'i-lucide:message-square'
}
],
[
{
label: 'More',
icon: 'i-lucide:circle-plus'
}
]
]
},
{
label: 'New team',
icon: 'i-lucide:plus',
kbds: ['meta', 'n']
}
],
[
{
label: 'GitHub',
icon: 'i-simple-icons:github',
to: 'https://github.com/nuxt/ui',
target: '_blank'
},
{
label: 'Support',
icon: 'i-lucide:life-buoy',
to: '/docs/pohon/components/dropdown-menu'
},
{
label: 'API',
icon: 'i-lucide:cloud',
disabled: true
}
],
[
{
label: 'Logout',
icon: 'i-lucide:log-out',
kbds: ['shift', 'meta', 'q']
}
]
])
</script>
<template>
<PDropdownMenu :items="items">
<PButton icon="i-lucide:menu" color="neutral" variant="outline" />
</PDropdownMenu>
</template>
Use the items prop as an array of objects with the following properties:
label?: stringicon?: stringavatar?: AvatarPropskbds?: string[] | KbdProps[]type?: "link" | "label" | "separator" | "checkbox"color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"checked?: booleandisabled?: booleanslot?: stringonSelect?: (e: Event) => voidonUpdateChecked?: (checked: boolean) => voidchildren?: DropdownMenuItem[] | DropdownMenuItem[][]class?: anypohon?: { item?: ClassNameValue, label?: ClassNameValue, separator?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLabel?: ClassNameValue, itemLabelExternalIcon?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingIcon?: ClassNameValue, itemTrailingKbds?: ClassNameValue, itemTrailingKbdsSize?: ClassNameValue }You can pass any property from the Link component such as to, target, etc.
<script setup lang="ts">
import type { PDropdownMenuItem } from 'pohon-ui'
const items = ref<PDropdownMenuItem[][]>([
[
{
label: 'Benjamin',
avatar: {
src: 'https://github.com/praburangki.png'
},
type: 'label'
}
],
[
{
label: 'Profile',
icon: 'i-lucide:user'
},
{
label: 'Billing',
icon: 'i-lucide:credit-card'
},
{
label: 'Settings',
icon: 'i-lucide:cog',
kbds: [',']
},
{
label: 'Keyboard shortcuts',
icon: 'i-lucide:monitor'
}
],
[
{
label: 'Team',
icon: 'i-lucide:users'
},
{
label: 'Invite users',
icon: 'i-lucide:user-plus',
children: [
[
{
label: 'Email',
icon: 'i-lucide:mail'
},
{
label: 'Message',
icon: 'i-lucide:message-square'
}
],
[
{
label: 'More',
icon: 'i-lucide:circle-plus'
}
]
]
},
{
label: 'New team',
icon: 'i-lucide:plus',
kbds: ['meta', 'n']
}
],
[
{
label: 'GitHub',
icon: 'i-simple-icons:github',
to: 'https://github.com/nuxt/ui',
target: '_blank'
},
{
label: 'Support',
icon: 'i-lucide:life-buoy',
to: '/docs/pohon/components/dropdown-menu'
},
{
label: 'API',
icon: 'i-lucide:cloud',
disabled: true
}
],
[
{
label: 'Logout',
icon: 'i-lucide:log-out',
kbds: ['shift', 'meta', 'q']
}
]
])
</script>
<template>
<PDropdownMenu
:items="items"
:pohon="{
content: 'w-48'
}"
>
<PButton icon="i-lucide:menu" color="neutral" variant="outline" />
</PDropdownMenu>
</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.Use the content prop to control how the DropdownMenu content is rendered, like its align or side for example.
<script setup lang="ts">
import type { PDropdownMenuItem } from 'pohon-ui'
const items = ref<PDropdownMenuItem[]>([
{
label: 'Profile',
icon: 'i-lucide:user'
},
{
label: 'Billing',
icon: 'i-lucide:credit-card'
},
{
label: 'Settings',
icon: 'i-lucide:cog'
}
])
</script>
<template>
<PDropdownMenu
:items="items"
:content="{
align: 'start',
side: 'bottom',
sideOffset: 8
}"
:pohon="{
content: 'w-48'
}"
>
<PButton label="Open" icon="i-lucide:menu" color="neutral" variant="outline" />
</PDropdownMenu>
</template>
Use the arrow prop to display an arrow on the DropdownMenu.
<script setup lang="ts">
import type { PDropdownMenuItem } from 'pohon-ui'
const items = ref<PDropdownMenuItem[]>([
{
label: 'Profile',
icon: 'i-lucide:user'
},
{
label: 'Billing',
icon: 'i-lucide:credit-card'
},
{
label: 'Settings',
icon: 'i-lucide:cog'
}
])
</script>
<template>
<PDropdownMenu
arrow
:items="items"
:pohon="{
content: 'w-48'
}"
>
<PButton label="Open" icon="i-lucide:menu" color="neutral" variant="outline" />
</PDropdownMenu>
</template>
Use the size prop to control the size of the DropdownMenu.
<script setup lang="ts">
import type { PDropdownMenuItem } from 'pohon-ui'
const items = ref<PDropdownMenuItem[]>([
{
label: 'Profile',
icon: 'i-lucide:user'
},
{
label: 'Billing',
icon: 'i-lucide:credit-card'
},
{
label: 'Settings',
icon: 'i-lucide:cog'
}
])
</script>
<template>
<PDropdownMenu
size="xl"
:items="items"
:content="{
align: 'start'
}"
:pohon="{
content: 'w-48'
}"
>
<PButton size="xl" label="Open" icon="i-lucide:menu" color="neutral" variant="outline" />
</PDropdownMenu>
</template>
size prop will not be proxied to the Button, you need to set it yourself.Use the modal prop to control whether the DropdownMenu blocks interaction with outside content. Defaults to true.
<script setup lang="ts">
import type { PDropdownMenuItem } from 'pohon-ui'
const items = ref<PDropdownMenuItem[]>([
{
label: 'Profile',
icon: 'i-lucide:user'
},
{
label: 'Billing',
icon: 'i-lucide:credit-card'
},
{
label: 'Settings',
icon: 'i-lucide:cog'
}
])
</script>
<template>
<PDropdownMenu
:items="items"
:pohon="{
content: 'w-48'
}"
>
<PButton label="Open" icon="i-lucide:menu" color="neutral" variant="outline" />
</PDropdownMenu>
</template>
Use the disabled prop to disable the DropdownMenu.
<script setup lang="ts">
import type { PDropdownMenuItem } from 'pohon-ui'
const items = ref<PDropdownMenuItem[]>([
{
label: 'Profile',
icon: 'i-lucide:user'
},
{
label: 'Billing',
icon: 'i-lucide:credit-card'
},
{
label: 'Settings',
icon: 'i-lucide:cog'
}
])
</script>
<template>
<PDropdownMenu
disabled
:items="items"
:pohon="{
content: 'w-48'
}"
>
<PButton label="Open" icon="i-lucide:menu" color="neutral" variant="outline" />
</PDropdownMenu>
</template>
You can use the type property with checkbox and use the checked / onUpdateChecked properties to control the checked state of the item.
<script setup lang="ts">
import type { PDropdownMenuItem } from 'pohon-ui';
import { computed, ref } from 'vue';
const showBookmarks = ref(true);
const showHistory = ref(false);
const showDownloads = ref(false);
const items = computed(() => [{
label: 'Interface',
icon: 'i-lucide:app-window',
type: 'label' as const,
}, {
type: 'separator' as const,
}, {
label: 'Show Bookmarks',
icon: 'i-lucide:bookmark',
type: 'checkbox' as const,
checked: showBookmarks.value,
onUpdateChecked(checked: boolean) {
showBookmarks.value = checked;
},
onSelect(e: Event) {
e.preventDefault();
},
}, {
label: 'Show History',
icon: 'i-lucide:clock',
type: 'checkbox' as const,
checked: showHistory.value,
onUpdateChecked(checked: boolean) {
showHistory.value = checked;
},
}, {
label: 'Show Downloads',
icon: 'i-lucide:download',
type: 'checkbox' as const,
checked: showDownloads.value,
onUpdateChecked(checked: boolean) {
showDownloads.value = checked;
},
}] satisfies Array<PDropdownMenuItem>);
</script>
<template>
<PDropdownMenu
:items="items"
:content="{ align: 'start' }"
:pohon="{ content: 'w-48' }"
>
<PButton
label="Open"
color="neutral"
variant="outline"
icon="i-lucide:menu"
/>
</PDropdownMenu>
</template>
checked state of items, it's recommended to wrap your items array inside a computed.You can use the color property to highlight certain items with a color.
<script setup lang="ts">
import type { PDropdownMenuItem } from 'pohon-ui';
const items: Array<Array<PDropdownMenuItem>> = [
[
{
label: 'View',
icon: 'i-lucide:eye',
},
{
label: 'Copy',
icon: 'i-lucide:copy',
},
{
label: 'Edit',
icon: 'i-lucide:pencil',
},
],
[
{
label: 'Delete',
color: 'error',
icon: 'i-lucide:trash',
},
],
];
</script>
<template>
<PDropdownMenu
:items="items"
:pohon="{ content: 'w-48' }"
>
<PButton
label="Open"
color="neutral"
variant="outline"
icon="i-lucide:menu"
/>
</PDropdownMenu>
</template>
You can control the open state by using the default-open prop or the v-model:open directive.
<script setup lang="ts">
import type { PDropdownMenuItem } from 'pohon-ui';
import { defineShortcuts } from '#imports';
import { ref } from 'vue';
const open = ref(false);
defineShortcuts({
o: () => {
open.value = !open.value;
},
});
const items: Array<PDropdownMenuItem> = [
{
label: 'Profile',
icon: 'i-lucide:user',
},
{
label: 'Billing',
icon: 'i-lucide:credit-card',
},
{
label: 'Settings',
icon: 'i-lucide:cog',
},
];
</script>
<template>
<PDropdownMenu
v-model:open="open"
:items="items"
:pohon="{ content: 'w-48' }"
>
<PButton
label="Open"
color="neutral"
variant="outline"
icon="i-lucide:menu"
/>
</PDropdownMenu>
</template>
defineShortcuts, you can toggle the DropdownMenu by pressing O.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">
import type { PDropdownMenuItem } from 'pohon-ui';
const items = [
{
label: 'Profile',
icon: 'i-lucide:user',
slot: 'profile' as const,
},
{
label: 'Billing',
icon: 'i-lucide:credit-card',
},
{
label: 'Settings',
icon: 'i-lucide:cog',
},
] satisfies Array<PDropdownMenuItem>;
</script>
<template>
<PDropdownMenu
:items="items"
:pohon="{ content: 'w-48' }"
>
<PButton
label="Open"
color="neutral"
variant="outline"
icon="i-lucide:menu"
/>
<template #profile-trailing>
<PIcon
name="i-lucide:badge-check"
class="text-primary shrink-0 size-5"
/>
</template>
</PDropdownMenu>
</template>
#item, #item-leading, #item-label and #item-trailing slots to customize all items.You can expand the content to the full width of its button by adding the w-$akar-dropdown-menu-trigger-width class on the pohon.content slot.
<script setup lang="ts">
import type { PDropdownMenuItem } from 'pohon-ui';
const items: Array<Array<PDropdownMenuItem>> = [
[
{
label: 'View',
icon: 'i-lucide:eye',
},
{
label: 'Copy',
icon: 'i-lucide:copy',
},
{
label: 'Edit',
icon: 'i-lucide:pencil',
},
],
[
{
label: 'Delete',
color: 'error',
icon: 'i-lucide:trash',
},
],
];
</script>
<template>
<PDropdownMenu
:items="items"
:pohon="{ content: 'w-(--akar-dropdown-menu-trigger-width)' }"
>
<PButton
label="Open"
class="akar:w-46"
color="neutral"
variant="outline"
block
trailing-icon="i-lucide:chevron-down"
/>
</PDropdownMenu>
</template>
app.config.ts:export default defineAppConfig({
pohon: {
dropdownMenu: {
slots: {
content: 'w-$akar-dropdown-menu-trigger-width'
}
}
}
})
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">
import type { DropdownMenuItem } from 'pohon-ui';
const items: Array<DropdownMenuItem> = [{
label: 'Invite users',
icon: 'i-lucide:user-plus',
children: [{
label: 'Invite by email',
icon: 'i-lucide:send-horizontal',
kbds: ['meta', 'e'],
onSelect() {
console.log('Invite by email clicked');
}
}, {
label: 'Invite by link',
icon: 'i-lucide:link',
kbds: ['meta', 'i'],
onSelect() {
console.log('Invite by link clicked');
}
}]
}, {
label: 'New team',
icon: 'i-lucide:plus',
kbds: ['meta', 'n'],
onSelect() {
console.log('New team clicked');
}
}];
defineShortcuts(extractShortcuts(items));
</script>
select function of the corresponding item.| Prop | Default | Type |
|---|
| Slot | Type |
|---|
| Event | Type |
|---|
Below is the theme configuration skeleton for the PDropdownMenu. Since the component is provided unstyled by default, you will need to fill in these values to apply your own custom look and feel. If you prefer to use our pre-built, opinionated styling, you can instead use our UnoCSS preset, this docs is using it as well.
export default defineAppConfig({
pohon: {
dropdownMenu: {
slots: {
content: '',
viewport: '',
arrow: '',
group: '',
label: '',
separator: '',
item: '',
itemLeadingIcon: '',
itemLeadingAvatar: '',
itemLeadingAvatarSize: '',
itemTrailing: '',
itemTrailingIcon: '',
itemTrailingKbds: '',
itemTrailingKbdsSize: '',
itemWrapper: '',
itemLabel: '',
itemDescription: '',
itemLabelExternalIcon: ''
},
variants: {
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
active: {
true: {
item: '',
itemLeadingIcon: ''
},
false: {
item: '',
itemLeadingIcon: ''
}
},
loading: {
true: {
itemLeadingIcon: ''
}
},
size: {
xs: {
label: '',
item: '',
itemLeadingIcon: '',
itemLeadingAvatarSize: '',
itemTrailingIcon: '',
itemTrailingKbds: '',
itemTrailingKbdsSize: ''
},
sm: {
label: '',
item: '',
itemLeadingIcon: '',
itemLeadingAvatarSize: '',
itemTrailingIcon: '',
itemTrailingKbds: '',
itemTrailingKbdsSize: ''
},
md: {
label: '',
item: '',
itemLeadingIcon: '',
itemLeadingAvatarSize: '',
itemTrailingIcon: '',
itemTrailingKbds: '',
itemTrailingKbdsSize: ''
},
lg: {
label: '',
item: '',
itemLeadingIcon: '',
itemLeadingAvatarSize: '',
itemTrailingIcon: '',
itemTrailingKbds: '',
itemTrailingKbdsSize: ''
},
xl: {
label: '',
item: '',
itemLeadingIcon: '',
itemLeadingAvatarSize: '',
itemTrailingIcon: '',
itemTrailingKbds: '',
itemTrailingKbdsSize: ''
}
}
},
compoundVariants: [],
defaultVariants: {
size: 'md'
}
}
}
};
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import pohon from 'pohon-ui/vite'
export default defineAppConfig({
pohon: {
dropdownMenu: {
slots: {
content: '',
viewport: '',
arrow: '',
group: '',
label: '',
separator: '',
item: '',
itemLeadingIcon: '',
itemLeadingAvatar: '',
itemLeadingAvatarSize: '',
itemTrailing: '',
itemTrailingIcon: '',
itemTrailingKbds: '',
itemTrailingKbdsSize: '',
itemWrapper: '',
itemLabel: '',
itemDescription: '',
itemLabelExternalIcon: ''
},
variants: {
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
active: {
true: {
item: '',
itemLeadingIcon: ''
},
false: {
item: '',
itemLeadingIcon: ''
}
},
loading: {
true: {
itemLeadingIcon: ''
}
},
size: {
xs: {
label: '',
item: '',
itemLeadingIcon: '',
itemLeadingAvatarSize: '',
itemTrailingIcon: '',
itemTrailingKbds: '',
itemTrailingKbdsSize: ''
},
sm: {
label: '',
item: '',
itemLeadingIcon: '',
itemLeadingAvatarSize: '',
itemTrailingIcon: '',
itemTrailingKbds: '',
itemTrailingKbdsSize: ''
},
md: {
label: '',
item: '',
itemLeadingIcon: '',
itemLeadingAvatarSize: '',
itemTrailingIcon: '',
itemTrailingKbds: '',
itemTrailingKbdsSize: ''
},
lg: {
label: '',
item: '',
itemLeadingIcon: '',
itemLeadingAvatarSize: '',
itemTrailingIcon: '',
itemTrailingKbds: '',
itemTrailingKbdsSize: ''
},
xl: {
label: '',
item: '',
itemLeadingIcon: '',
itemLeadingAvatarSize: '',
itemTrailingIcon: '',
itemTrailingKbds: '',
itemTrailingKbdsSize: ''
}
}
},
compoundVariants: [],
defaultVariants: {
size: 'md'
}
}
}
};
With Pohon UI, you can achieve similar component functionality with less code and effort, as it comes with built-in styles mechanism and behaviors that are optimized for common use cases. Since it's using unocss-variants it adds a runtime cost, but it can be worth it if you prioritize development speed and ease of use over fine-grained control.
If this is a deal breaker for you, you can always stick to using Akar and build your own custom components on top of it.