Usage
Use a v-model to display a searchable and selectable list of commands.
<script setup lang="ts">
const people = [
{ id: 1, label: 'Wade Cooper' },
{ id: 2, label: 'Arlene Mccoy' },
{ id: 3, label: 'Devon Webb' },
{ id: 4, label: 'Tom Cook' },
{ id: 5, label: 'Tanya Fox' },
{ id: 6, label: 'Hellen Schmidt' },
{ id: 7, label: 'Caroline Schultz' },
{ id: 8, label: 'Mason Heaney' },
{ id: 9, label: 'Claudie Smitham' },
{ id: 10, label: 'Emil Schaefer' }
]
const selected = ref([people[3]])
</script>
<template>
<UCommandPalette
v-model="selected"
multiple
nullable
:autoselect="false"
:groups="[{ key: 'people', commands: people }]"
:fuse="{ resultLimit: 6, fuseOptions: { threshold: 0.1 } }"
/>
</template>
You can put a CommandPalette anywhere you want but it's most commonly used inside of a modal.
<script setup lang="ts">
const isOpen = ref(false)
const people = [
{ id: 1, label: 'Wade Cooper' },
{ id: 2, label: 'Arlene Mccoy' },
{ id: 3, label: 'Devon Webb' },
{ id: 4, label: 'Tom Cook' },
{ id: 5, label: 'Tanya Fox' },
{ id: 6, label: 'Hellen Schmidt' },
{ id: 7, label: 'Caroline Schultz' },
{ id: 8, label: 'Mason Heaney' },
{ id: 9, label: 'Claudie Smitham' },
{ id: 10, label: 'Emil Schaefer' }
]
const selected = ref([])
</script>
<template>
<div>
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="isOpen">
<UCommandPalette
v-model="selected"
multiple
nullable
:groups="[{ key: 'people', commands: people }]"
/>
</UModal>
</div>
</template>
You can pass multiple groups of commands to the component. Each group will be separated by a divider and will display a label.
Without a v-model, you can also listen on @update:model-value to navigate to a link or do something else when a command is clicked.
Recent searches
<script setup lang="ts">
const router = useRouter()
const toast = useToast()
const commandPaletteRef = ref()
const users = [
{ id: 'benjamincanac', label: 'benjamincanac', href: 'https://github.com/benjamincanac', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/benjamincanac', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/benjamincanac 2x', loading: 'lazy' } },
{ id: 'Atinux', label: 'Atinux', href: 'https://github.com/Atinux', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/Atinux', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/Atinux 2x', loading: 'lazy' } },
{ id: 'smarroufin', label: 'smarroufin', href: 'https://github.com/smarroufin', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/smarroufin', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/smarroufin 2x', loading: 'lazy' } }
]
const actions = [
{ id: 'new-file', label: 'Add new file', icon: 'i-heroicons-document-plus', click: () => toast.add({ title: 'New file added!' }), shortcuts: ['⌘', 'N'] },
{ id: 'new-folder', label: 'Add new folder', icon: 'i-heroicons-folder-plus', click: () => toast.add({ title: 'New folder added!' }), shortcuts: ['⌘', 'F'] },
{ id: 'hashtag', label: 'Add hashtag', icon: 'i-heroicons-hashtag', click: () => toast.add({ title: 'Hashtag added!' }), shortcuts: ['⌘', 'H'] },
{ id: 'label', label: 'Add label', icon: 'i-heroicons-tag', click: () => toast.add({ title: 'Label added!' }), shortcuts: ['⌘', 'L'] }
]
const groups = computed(() =>
[commandPaletteRef.value?.query ? {
key: 'users',
commands: users
} : {
key: 'recent',
label: 'Recent searches',
commands: users.slice(0, 1)
}, {
key: 'actions',
commands: actions
}].filter(Boolean))
function onSelect (option) {
if (option.click) {
option.click()
} else if (option.to) {
router.push(option.to)
} else if (option.href) {
window.open(option.href, '_blank')
}
}
</script>
<template>
<UCommandPalette ref="commandPaletteRef" :groups="groups" :autoselect="false" @update:model-value="onSelect" />
</template>
Icon
Use any icon from Iconify by setting the icon prop by using this pattern: i-{collection_name}-{icon_name}.
Use the selected-icon prop to set a different icon or change it globally in ui.commandPalette.default.selectedIcon. Defaults to i-heroicons-check-20-solid.
<template>
<UCommandPalette icon="i-heroicons-command-line" />
</template>
Loading
Use the loading prop to show a loading icon.
Use the loading-icon prop to set a different icon or change it globally in ui.commandPalette.default.loadingIcon. Defaults to i-heroicons-arrow-path-20-solid.
<template>
<UCommandPalette loading />
</template>
Placeholder
Use the placeholder prop to change the input placeholder
<template>
<UCommandPalette placeholder="Type a command..." />
</template>
Close
Use the close-button prop to display a close button on the right side of the input.
You can pass all the props of the Button component to customize it through the close-button prop or globally through ui.commandPalette.default.closeButton.
<template>
<UCommandPalette
:close-button="{ icon: 'i-heroicons-x-mark-20-solid', color: 'gray', variant: 'link', padded: false }"
/>
</template>
Empty
An empty state will be displayed when there are no results.
Use the empty-state prop to customize the icon and label or change them globally in ui.commandPalette.default.emptyState.
You can also set it to null to hide the empty state.
We couldn't find any items.
<template>
<UCommandPalette
:empty-state="{ icon: 'i-heroicons-magnifying-glass-20-solid', label: 'We couldn't find any items.', queryLabel: 'We couldn't find any items with that term. Please try again.' }"
placeholder="Type something to see the empty label change"
/>
</template>
Full-text search
The CommandPalette component takes care of the full-text search for you with Fuse.js. You can pass all the options of Fuse.js through the fuse prop.
When searching for a command, the component will look for a label property on the command by default. You can customize this behavior by overriding the command-attribute prop. This will also affect the display of the command.
You can also highlight the matches in the command by setting the fuse.fuseOptions.includeMatches to true. The CommandPalette component automatically takes care of the highlighting for you.
<template>
<UCommandPalette
command-attribute="title"
:fuse="{
fuseOptions: {
ignoreLocation: true,
includeMatches: true,
threshold: 0,
keys: ['title', 'description', 'children.children.value', 'children.children.children.value']
},
resultLimit: 10
}"
/>
</template>
Async search
You can also pass an async function to the search property of a group to perform an async search. The function will receive the query as its first argument and should return an array of commands.
We couldn't find any items.
<script setup lang="ts">
const groups = [{
key: 'users',
label: q => q && `Users matching “${q}”...`,
search: async (q) => {
if (!q) {
return []
}
const users = await $fetch<any[]>('https://jsonplaceholder.typicode.com/users', { params: { q } })
return users.map(user => ({ id: user.id, label: user.name, suffix: user.email }))
}
}]
</script>
<template>
<UCommandPalette :groups="groups" :autoselect="false" />
</template>
loading state will automatically be enabled when a search function is loading. You can disable this behavior by setting the loading-icon prop to null or globally in ui.commandPalette.default.loadingIcon.Filter search
You can also pass a function to the filter property of a group to filter displayed commands after the search happened. The function will receive the query as its first argument, the array of commands as second argument and should return an array of commands.
<script setup lang="ts">
const people = [
{ id: 1, label: 'Wade Cooper', child: true },
{ id: 2, label: 'Arlene Mccoy' },
{ id: 3, label: 'Devon Webb', child: true },
{ id: 4, label: 'Tom Cook' },
{ id: 5, label: 'Tanya Fox', child: true },
{ id: 6, label: 'Hellen Schmidt' },
{ id: 7, label: 'Caroline Schultz', child: true },
{ id: 8, label: 'Mason Heaney' },
{ id: 9, label: 'Claudie Smitham', child: true },
{ id: 10, label: 'Emil Schaefer' }
]
const groups = [{
key: 'users',
commands: people,
filter: (q, commands) => {
if (!q) {
return commands?.filter(command => !command.child)
}
return commands
}
}]
</script>
<template>
<UCommandPalette :groups="groups" :autoselect="false" />
</template>
Slots
<group>-icon
Use the #<group>-icon slot to override the left command content which display by default the icon, avatar and chip.
<group>-command
Use the #<group>-command slot to override the command content which display by default the prefix, suffix and label (customizable through the command-attribute prop).
<group>-active
Use the #<group>-active slot to override the right command content (when hovered) which display by default the active field of the group if provided.
<group>-inactive
Use the #<group>-inactive slot to override the right command content (when not hovered) which display by default the inactive field of the group if provided or the shortcuts of the command.
group, command, active and selected properties in the slot scope.empty-state
Use the #empty-state slot to customize the empty state.
<template>
<UCommandPalette>
<template #empty-state>
<div class="flex flex-col items-center justify-center py-6 gap-3">
<span class="italic text-sm">Nothing here!</span>
<UButton label="Add item" />
</div>
</template>
</UCommandPalette>
</template>
Props
{}config.default.iconnull"Search..."config.default.loadingIcon"id"config.default.selectedIcon200config.default.emptyState"label""label"[]config.default.closeButton as unknown as Button{}falsefalsefalsetruetruetrueAPI
When accessing the component via a template ref, you can use the following:
The current query.
Updates the current query.
The results exposed by useFuse.
The Combobox API exposed by Headless UI.
Config
{
wrapper: 'flex flex-col flex-1 min-h-0 divide-y divide-gray-100 dark:divide-gray-800',
container: 'relative flex-1 overflow-y-auto divide-y divide-gray-100 dark:divide-gray-800 scroll-py-2',
input: {
wrapper: 'relative flex items-center',
base: 'w-full placeholder-gray-400 dark:placeholder-gray-500 bg-transparent border-0 text-gray-900 dark:text-white focus:ring-0 focus:outline-none',
padding: 'px-4',
height: 'h-12',
size: 'sm:text-sm',
icon: {
base: 'pointer-events-none absolute start-4 text-gray-400 dark:text-gray-500',
loading: 'animate-spin',
size: 'h-5 w-5',
padding: 'ps-11',
},
closeButton: {
base: 'absolute end-4',
padding: 'pe-10',
},
},
emptyState: {
wrapper: 'flex flex-col items-center justify-center flex-1 px-6 py-14 sm:px-14',
label: 'text-sm text-center text-gray-900 dark:text-white',
queryLabel: 'text-sm text-center text-gray-900 dark:text-white',
icon: 'w-6 h-6 mx-auto text-gray-400 dark:text-gray-500 mb-4',
},
group: {
wrapper: 'p-2',
label: 'px-2.5 my-2 text-xs font-semibold text-gray-900 dark:text-white',
container: 'text-sm text-gray-700 dark:text-gray-200',
command: {
base: 'flex justify-between select-none items-center rounded-md px-2.5 py-1.5 gap-2 relative',
active: 'bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white',
inactive: '',
label: 'flex items-center gap-1.5 min-w-0',
prefix: 'text-gray-400 dark:text-gray-500',
suffix: 'text-gray-400 dark:text-gray-500',
container: 'flex items-center gap-1.5 min-w-0',
icon: {
base: 'flex-shrink-0 w-5 h-5',
active: 'text-gray-900 dark:text-white',
inactive: 'text-gray-400 dark:text-gray-500',
},
selectedIcon: {
base: 'h-5 w-5 text-gray-900 dark:text-white flex-shrink-0',
},
avatar: {
base: 'flex-shrink-0',
size: '2xs',
},
chip: {
base: 'flex-shrink-0 w-2 h-2 mx-1 rounded-full',
},
disabled: 'opacity-50',
shortcuts: 'hidden md:inline-flex flex-shrink-0 gap-0.5',
},
active: 'flex-shrink-0 text-gray-500 dark:text-gray-400',
inactive: 'flex-shrink-0 text-gray-500 dark:text-gray-400',
},
default: {
icon: 'i-heroicons-magnifying-glass-20-solid',
loadingIcon: 'i-heroicons-arrow-path-20-solid',
emptyState: {
icon: 'i-heroicons-magnifying-glass-20-solid',
label: "We couldn't find any items.",
queryLabel: "We couldn't find any items with that term. Please try again.",
},
closeButton: null,
selectedIcon: 'i-heroicons-check-20-solid',
},
}
Example
Here are a few examples illustrating how you can customize the appearance of the CommandPalette component by utilizing its ui prop.
Algolia
No recent searches