Usage
Use a v-model
to display a searchable and selectable list of commands.
<script setup>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 :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>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>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://avatars.githubusercontent.com/u/739984?v=4' } }, { id: 'Atinux', label: 'Atinux', href: 'https://github.com/Atinux', target: '_blank', avatar: { src: 'https://avatars.githubusercontent.com/u/904724?v=4' } }, { id: 'smarroufin', label: 'smarroufin', href: 'https://github.com/smarroufin', target: '_blank', avatar: { src: 'https://avatars.githubusercontent.com/u/7547335?v=4' } }]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" @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
.
We couldn't find any items.
<UCommandPalette icon="i-heroicons-command-line" />
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
.
We couldn't find any items.
<UCommandPalette loading />
Placeholder
Use the placeholder
prop to change the input placeholder
We couldn't find any items.
<UCommandPalette placeholder="Type a command..." />
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
.
We couldn't find any items.
<UCommandPalette :close-button="{ icon: 'i-heroicons-x-mark-20-solid', color: 'gray', variant: 'link', padded: false }"/>
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.
<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"/>
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>const groups = computed(() => { return [{ key: 'users', label: q => q && `Users matching “${q}”...`, search: async (q) => { if (!q) { return [] } const users = await $fetch(`https://jsonplaceholder.typicode.com/users`, { params: { q } }) return users.map(user => ({ id: user.id, label: user.name, suffix: user.email })) } }].filter(Boolean)})</script><template> <UCommandPalette :groups="groups" /></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
.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.
<script setup>const groups = [...]</script><template> <UCommandPalette :groups="groups"> <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.icon
undefined
null
"Search..."
config.default.loadingIcon
"id"
config.default.selectedIcon
200
config.default.emptyState
"label"
"label"
[]
config.default.closeButton as Button
{}
false
false
false
true
true
true
Preset
{ "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", "size": "h-4 w-4", "padding": "ps-10" }, "closeButton": "absolute end-4" }, "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 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 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-2 min-w-0", "icon": { "base": "flex-shrink-0 w-4 h-4", "active": "text-gray-900 dark:text-white", "inactive": "text-gray-400 dark:text-gray-500" }, "selectedIcon": { "base": "h-4 w-4 text-gray-900 dark:text-white flex-shrink-0" }, "avatar": { "base": "flex-shrink-0", "size": "3xs" }, "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" }}