The Editor component is now implemented! Check it out.
v2.1.16
/
  • Get Started
  • Components
  • Composables
  • Typography
  • GitHub
  • Layout
  • App
  • Container
  • Error
  • SidebarLayout
  • Element
  • Advice
  • Alert
  • Avatar
  • AvatarGroup
  • Badge
  • Banner
  • Button
  • Calendar
  • Card
  • Chip
  • Collapsible
  • Countdown
  • FieldGroup
  • Kbd
  • Progress
  • Separator
  • Skeleton
  • Form
  • Checkbox
  • CheckboxGroup
  • ColorPicker
  • FileUpload
  • Form
  • FormField
  • Input
  • InputDate
  • InputMenu
  • InputNumber
  • InputTags
  • InputTime
  • PinInput
  • RadioGroup
  • Range
  • Select
  • SelectMenu
  • Switch
  • Textarea
  • Data
  • Accordion
  • DescriptionList
  • Empty
  • Table
  • TableWrapper
  • Timeline
  • User
  • Navigation
  • Breadcrumb
  • CommandPalette
  • Link
  • NavigationMenu
  • Pagination
  • Stepper
  • Tabs
  • Overlay
  • ContextMenu
  • DropdownMenu
  • Modal
  • Popover
  • Slideover
  • Toast
  • Tooltip
  • Page
  • PageCard
  • PageColumns
  • PageGrid
  • PageLinks
  • PageList
  • Dashboard
  • DashboardGroup
  • DashboardSearch
  • DashboardSearchButton
  • AI Chat
  • soonChatMessage
  • soonChatMessages
  • soonChatPalette
  • soonChatPrompt
  • soonChatPromptSubmit
  • Editor
  • NewEditor
  • NewEditorDragHandle
  • NewEditorEmojiMenu
  • NewEditorMentionMenu
  • NewEditorSuggestionMenu
  • NewEditorToolbar
  • Content
  • ContentSearch
  • ContentSearchButton
  • ContentSurround
  • ContentToc
  • Color Mode
  • ColorModeAvatar
  • ColorModeButton
  • ColorModeImage
  • ColorModeSelect
  • ColorModeSwitch
  • i18n
  • LocaleSelect
  • b24icons
  • b24jssdk
Use our Nuxt starter
v2.1.16
  • Docs
  • Components
  • Composables
  • Typography

CommandPalette

A searchable command palette powered by Fuse.js for fast, fuzzy text search.
GitHub
Demo
Nuxt UI
Fuse.jsFuse.js
ListboxListbox

Usage

Use the v-model directive to control the value of the CommandPalette or the default-value prop to set the initial value when you do not need to control its state.

Users
<script setup lang="ts">
const groups = ref([
  {
    id: 'users',
    label: 'Users',
    items: [
      {
        label: 'Assistant Name',
        suffix: 'assistant',
        avatar: {
          src: '/b24ui/avatar/assistant.png'
        }
      },
      {
        label: 'Bitrix24',
        suffix: 'bitrix24',
        avatar: {
          src: 'https://github.com/bitrix24.png'
        }
      },
      {
        label: 'Employee Name',
        suffix: 'employee',
        avatar: {
          src: '/b24ui/avatar/employee.png'
        }
      }
    ]
  }
])
const value = ref({})
</script>

<template>
  <B24CommandPalette v-model="value" :groups="groups" class="flex-1" />
</template>
You can also use the @update:model-value event to listen to the selected item(s).

Groups

The CommandPalette component filters groups and ranks matching commands by relevance as users type. It provides dynamic, instant search results for efficient command discovery. Use the groups prop as an array of objects with the following properties:

  • id: string
  • label?: string
  • slot?: string
  • items?: CommandPaletteItem[]
  • ignoreFilter?: boolean
  • postFilter?: (searchTerm: string, items: T[]) => T[]
  • highlightedIcon?: IconComponent
You must provide an id for each group otherwise the group will be ignored.

Each group contains an items array of objects that define the commands. Each item can have the following properties:

  • prefix?: string
  • label?: string
  • suffix?: string
  • icon?: IconComponent
  • avatar?: AvatarProps
  • chip?: ChipProps
  • kbds?: string[] | KbdProps[]
  • active?: boolean
  • loading?: boolean
  • disabled?: boolean
  • slot?: string
  • placeholder?: string
  • children?: CommandPaletteItem[]
  • onSelect?: (e: Event) => void
  • class?: any
  • b24ui?: { item?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLeadingChipSize?: ClassNameValue, itemLeadingChip?: ClassNameValue, itemLabel?: ClassNameValue, itemLabelPrefix?: ClassNameValue, itemLabelBase?: ClassNameValue, itemLabelSuffix?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingKbds?: ClassNameValue, itemTrailingKbdsSize?: ClassNameValue, itemTrailingHighlightedIcon?: ClassNameValue, itemTrailingIcon?: ClassNameValue }

You can pass any property from the Link component such as to, target, etc.

Users
<script setup lang="ts">
const groups = ref([
  {
    id: 'users',
    label: 'Users',
    items: [
      {
        label: 'Assistant Name',
        suffix: 'assistant',
        avatar: {
          src: '/b24ui/avatar/assistant.png'
        }
      },
      {
        label: 'Bitrix24',
        suffix: 'bitrix24',
        avatar: {
          src: 'https://github.com/bitrix24.png'
        }
      },
      {
        label: 'Employee Name',
        suffix: 'employee',
        avatar: {
          src: '/b24ui/avatar/employee.png'
        }
      }
    ]
  }
])
const value = ref({})
</script>

<template>
  <B24CommandPalette v-model="value" :groups="groups" class="flex-1" />
</template>
Each item can take a children array of objects with the following properties to create submenus:

Multiple

Use the multiple prop to allow multiple selections.

Users
<script setup lang="ts">
const groups = ref([
  {
    id: 'users',
    label: 'Users',
    items: [
      {
        label: 'Assistant Name',
        suffix: 'assistant',
        avatar: {
          src: '/b24ui/avatar/assistant.png'
        }
      },
      {
        label: 'Bitrix24',
        suffix: 'bitrix24',
        avatar: {
          src: 'https://github.com/bitrix24.png'
        }
      },
      {
        label: 'Employee Name',
        suffix: 'employee',
        avatar: {
          src: '/b24ui/avatar/employee.png'
        }
      }
    ]
  }
])
const value = ref([])
</script>

<template>
  <B24CommandPalette multiple v-model="value" :groups="groups" class="flex-1" />
</template>
Ensure to pass an array to the default-value prop or the v-model directive.

Placeholder

Use the placeholder prop to change the placeholder text.

<script setup lang="ts">
const groups = ref([
  {
    id: 'apps',
    items: [
      {
        label: 'Calendar'
      },
      {
        label: 'Music'
      },
      {
        label: 'Maps'
      }
    ]
  }
])
</script>

<template>
  <B24CommandPalette placeholder="Search an app..." :groups="groups" class="flex-1" />
</template>

Icon

Use the icon prop to customize the input Icon.

<script setup lang="ts">
import RocketIcon from '@bitrix24/b24icons-vue/main/RocketIcon'

const groups = ref([
  {
    id: 'apps',
    items: [
      {
        label: 'Calendar'
      },
      {
        label: 'Music'
      },
      {
        label: 'Maps'
      }
    ]
  }
])
</script>

<template>
  <B24CommandPalette :icon="RocketIcon" :groups="groups" class="flex-1" />
</template>

Selected Icon

Use the selected-icon prop to customize the selected item Icon.

Users
<script setup lang="ts">
import RocketIcon from '@bitrix24/b24icons-vue/main/RocketIcon'

const groups = ref([
  {
    id: 'users',
    label: 'Users',
    items: [
      {
        label: 'Assistant Name',
        suffix: 'assistant',
        avatar: {
          src: '/b24ui/avatar/assistant.png'
        }
      },
      {
        label: 'Bitrix24',
        suffix: 'bitrix24',
        avatar: {
          src: 'https://github.com/bitrix24.png'
        }
      },
      {
        label: 'Employee Name',
        suffix: 'employee',
        avatar: {
          src: '/b24ui/avatar/employee.png'
        }
      }
    ]
  }
])
const value = ref([
  {
    label: 'Bitrix24',
    suffix: 'bitrix24',
    avatar: {
      src: 'https://github.com/bitrix24.png'
    }
  }
])
</script>

<template>
  <B24CommandPalette multiple v-model="value" :selected-icon="RocketIcon" :groups="groups" class="flex-1" />
</template>

Trailing Icon

Use the trailing-icon prop to customize the trailing Icon when an item has children.

<script setup lang="ts">
import RocketIcon from '@bitrix24/b24icons-vue/main/RocketIcon'

const groups = ref([
  {
    id: 'actions',
    items: [
      {
        label: 'Share',
        children: [
          {
            label: 'Email'
          },
          {
            label: 'Copy'
          },
          {
            label: 'Link'
          }
        ]
      }
    ]
  }
])
</script>

<template>
  <B24CommandPalette :trailing-icon="RocketIcon" :groups="groups" class="flex-1" />
</template>

Loading

Use the loading prop to show a loading icon on the CommandPalette.

<script setup lang="ts">
const groups = ref([
  {
    id: 'apps',
    items: [
      {
        label: 'Calendar'
      },
      {
        label: 'Music'
      },
      {
        label: 'Maps'
      }
    ]
  }
])
</script>

<template>
  <B24CommandPalette loading :groups="groups" class="flex-1" />
</template>

Close

Use the close prop to display a Button to dismiss the CommandPalette.

An update:open event will be emitted when the close button is clicked.
<script setup lang="ts">
const groups = ref([
  {
    id: 'apps',
    items: [
      {
        label: 'Calendar'
      },
      {
        label: 'Music'
      },
      {
        label: 'Maps'
      }
    ]
  }
])
</script>

<template>
  <B24CommandPalette close :groups="groups" class="flex-1" />
</template>

You can pass any property from the Button component to customize it.

<script setup lang="ts">
const groups = ref([
  {
    id: 'apps',
    items: [
      {
        label: 'Calendar'
      },
      {
        label: 'Music'
      },
      {
        label: 'Maps'
      }
    ]
  }
])
</script>

<template>
  <B24CommandPalette
    :close="{
      color: 'air-primary',
      rounded: true,
      size: 'md'
    }"
    :groups="groups"
    class="flex-1"
  />
</template>

Close Icon

Use the close-icon prop to customize the close button Icon.

<script setup lang="ts">
import RocketIcon from '@bitrix24/b24icons-vue/main/RocketIcon'

const groups = ref([
  {
    id: 'apps',
    items: [
      {
        label: 'Calendar'
      },
      {
        label: 'Music'
      },
      {
        label: 'Maps'
      }
    ]
  }
])
</script>

<template>
  <B24CommandPalette close :close-icon="RocketIcon" :groups="groups" class="flex-1" />
</template>

Back

Use the back prop to customize or hide the back button (with false value) displayed when navigating into a submenu.

You can pass any property from the Button component to customize it.

<script setup lang="ts">
const groups = ref([
  {
    id: 'actions',
    items: [
      {
        label: 'Share',
        children: [
          {
            label: 'Email'
          },
          {
            label: 'Copy'
          },
          {
            label: 'Link'
          }
        ]
      }
    ]
  }
])
</script>

<template>
  <B24CommandPalette
    :back="{
      color: 'air-primary'
    }"
    :groups="groups"
    class="flex-1"
  />
</template>

Back Icon

Use the back-icon prop to customize the back button Icon.

<script setup lang="ts">
import RocketIcon from '@bitrix24/b24icons-vue/main/RocketIcon'

const groups = ref([
  {
    id: 'actions',
    items: [
      {
        label: 'Share',
        children: [
          {
            label: 'Email'
          },
          {
            label: 'Copy'
          },
          {
            label: 'Link'
          }
        ]
      }
    ]
  }
])
</script>

<template>
  <B24CommandPalette :back-icon="RocketIcon" :groups="groups" class="flex-1" />
</template>

Disabled

Use the disabled prop to disable the CommandPalette.

<script setup lang="ts">
const groups = ref([
  {
    id: 'apps',
    items: [
      {
        label: 'Calendar'
      },
      {
        label: 'Music'
      },
      {
        label: 'Maps'
      }
    ]
  }
])
</script>

<template>
  <B24CommandPalette disabled :groups="groups" class="flex-1" />
</template>

Examples

Control selected item(s)

You can control the selected item(s) by using the default-value prop or the v-model directive, by using the onSelect field on each item or by using the @update:model-value event.

Users
Assistant NameassistantBitrix24bitrix24Employee Nameemployee
<script setup lang="ts">
import FileUploadIcon from '@bitrix24/b24icons-vue/main/FileUploadIcon'
import FolderPlusIcon from '@bitrix24/b24icons-vue/outline/FolderPlusIcon'
import TagIcon from '@bitrix24/b24icons-vue/outline/TagIcon'
import StageIcon from '@bitrix24/b24icons-vue/outline/StageIcon'

const toast = useToast()

const groups = ref([
  {
    id: 'users',
    label: 'Users',
    items: [
      {
        label: 'Assistant Name',
        suffix: 'assistant',
        to: 'https://github.com/bitrix24',
        target: '_blank',
        avatar: {
          src: '/b24ui/avatar/assistant.png'
        }
      },
      {
        label: 'Bitrix24',
        suffix: 'bitrix24',
        to: 'https://github.com/bitrix24',
        target: '_blank',
        avatar: {
          src: 'https://github.com/bitrix24.png'
        }
      },
      {
        label: 'Employee Name',
        suffix: 'employee',
        to: 'https://github.com/bitrix24',
        avatarSrc: '/b24ui/avatar/employee.png',
        target: '_blank',
        avatar: {
          src: '/b24ui/avatar/employee.png'
        }
      }
    ]
  },
  {
    id: 'actions',
    items: [
      {
        label: 'Add new file',
        suffix: 'Create a new file in the current directory or workspace.',
        icon: FileUploadIcon,
        kbds: [
          'meta',
          'I'
        ],
        onSelect() {
          toast.add({ title: 'Add new file' })
        }
      },
      {
        label: 'Add new folder',
        suffix: 'Create a new folder in the current directory or workspace.',
        icon: FolderPlusIcon,
        kbds: [
          'meta',
          'F'
        ],
        onSelect() {
          toast.add({ title: 'Add new folder' })
        }
      },
      {
        label: 'Add hashtag',
        suffix: 'Add a hashtag to the current item.',
        icon: TagIcon,
        kbds: [
          'meta',
          'H'
        ],
        onSelect() {
          toast.add({ title: 'Add hashtag' })
        }
      },
      {
        label: 'Add label',
        suffix: 'Add a label to the current item.',
        icon: StageIcon,
        kbds: [
          'meta',
          'L'
        ],
        onSelect() {
          toast.add({ title: 'Add label' })
        }
      }
    ]
  }
])

function onSelect(item: any) {
  console.log(item)
}
</script>

<template>
  <B24CommandPalette
    :groups="groups"
    class="flex-1 h-[320px]"
    @update:model-value="onSelect"
  />
</template>

Control search term

Use the v-model:search-term directive to control the search term.

Bitrix24bitrix24
<script setup lang="ts">
const users = [
  {
    label: 'Assistant Name',
    suffix: 'assistant',
    to: 'https://github.com/bitrix24',
    target: '_blank',
    avatar: {
      src: '/b24ui/avatar/assistant.png'
    }
  },
  {
    label: 'Bitrix24',
    suffix: 'bitrix24',
    to: 'https://github.com/bitrix24',
    target: '_blank',
    avatar: {
      src: 'https://github.com/bitrix24.png'
    }
  },
  {
    label: 'Employee Name',
    suffix: 'employee',
    to: 'https://github.com/bitrix24',
    avatarSrc: '/b24ui/avatar/employee.png',
    target: '_blank',
    avatar: {
      src: '/b24ui/avatar/employee.png'
    }
  }
]

const searchTerm = ref('B')

function onSelect() {
  searchTerm.value = ''
}
</script>

<template>
  <B24CommandPalette
    v-model:search-term="searchTerm"
    :groups="[{ id: 'users', items: users }]"
    class="flex-1"
    @update:model-value="onSelect"
  />
</template>
This example uses the @update:model-value event to reset the search term when an item is selected.

With children in items

You can create hierarchical menus by using the children property in items. When an item has children, it will automatically display a chevron icon and enable navigation into a submenu.

Actions
<script setup lang="ts">
import CirclePlusIcon from '@bitrix24/b24icons-vue/outline/CirclePlusIcon'
import FileUploadIcon from '@bitrix24/b24icons-vue/main/FileUploadIcon'
import FolderPlusIcon from '@bitrix24/b24icons-vue/outline/FolderPlusIcon'
import AddProductIcon from '@bitrix24/b24icons-vue/outline/AddProductIcon'
import ShareIcon from '@bitrix24/b24icons-vue/button/ShareIcon'
import LinkIcon from '@bitrix24/b24icons-vue/outline/LinkIcon'
import MailSendIcon from '@bitrix24/b24icons-vue/outline/MailSendIcon'
import Share2Icon from '@bitrix24/b24icons-vue/main/Share2Icon'
import TwitterIcon from '@bitrix24/b24icons-vue/common-service/TwitterIcon'
import TelegramIcon from '@bitrix24/b24icons-vue/social/TelegramIcon'
import SettingsLIcon from '@bitrix24/b24icons-vue/outline/SettingsLIcon'
import Filter1Icon from '@bitrix24/b24icons-vue/main/Filter1Icon'
import PaletteIcon from '@bitrix24/b24icons-vue/outline/PaletteIcon'
import ShieldIcon from '@bitrix24/b24icons-vue/outline/ShieldIcon'

const toast = useToast()

const groups = [
  {
    id: 'actions',
    label: 'Actions',
    items: [
      {
        label: 'Create new',
        icon: CirclePlusIcon,
        children: [
          {
            label: 'New file',
            icon: FileUploadIcon,
            suffix: 'Create a new file in the current directory',
            onSelect(e: Event) {
              e.preventDefault()
              toast.add({ title: 'New file created!' })
            },
            kbds: ['meta', 'I']
          },
          {
            label: 'New folder',
            icon: FolderPlusIcon,
            suffix: 'Create a new folder in the current directory',
            onSelect(e: Event) {
              e.preventDefault()
              toast.add({ title: 'New folder created!' })
            },
            kbds: ['meta', 'F']
          },
          {
            label: 'New project',
            icon: AddProductIcon,
            suffix: 'Create a new project from a template',
            onSelect(e: Event) {
              e.preventDefault()
              toast.add({ title: 'New project created!' })
            },
            kbds: ['meta', 'P']
          }
        ]
      },
      {
        label: 'Share',
        icon: ShareIcon,
        children: [
          {
            label: 'Copy link',
            icon: LinkIcon,
            suffix: 'Copy a link to the current item',
            onSelect(e: Event) {
              e.preventDefault()
              toast.add({ title: 'Link copied to clipboard!' })
            },
            kbds: ['meta', 'L']
          },
          {
            label: 'Share via email',
            icon: MailSendIcon,
            suffix: 'Share the current item via email',
            onSelect(e: Event) {
              e.preventDefault()
              toast.add({ title: 'Share via email dialog opened!' })
            }
          },
          {
            label: 'Share on social',
            icon: Share2Icon,
            suffix: 'Share the current item on social media',
            children: [
              {
                label: 'Twitter',
                icon: TwitterIcon,
                onSelect(e: Event) {
                  e.preventDefault()
                  toast.add({ title: 'Shared on Twitter!' })
                }
              },
              {
                label: 'Telegram',
                icon: TelegramIcon,
                onSelect(e: Event) {
                  e.preventDefault()
                  toast.add({ title: 'Shared on Telegram!' })
                }
              }
            ]
          }
        ]
      },
      {
        label: 'Settings',
        icon: SettingsLIcon,
        children: [
          {
            label: 'General',
            icon: Filter1Icon,
            suffix: 'Configure general settings',
            onSelect(e: Event) {
              e.preventDefault()
              toast.add({ title: 'General settings opened!' })
            }
          },
          {
            label: 'Appearance',
            icon: PaletteIcon,
            suffix: 'Customize the appearance',
            onSelect(e: Event) {
              e.preventDefault()
              toast.add({ title: 'Appearance settings opened!' })
            }
          },
          {
            label: 'Security',
            icon: ShieldIcon,
            suffix: 'Manage security settings',
            onSelect(e: Event) {
              e.preventDefault()
              toast.add({ title: 'Security settings opened!' })
            }
          }
        ]
      }
    ]
  }
]
</script>

<template>
  <B24CommandPalette :groups="groups" class="flex-1" />
</template>
When navigating into a submenu:
  • The search term is reset
  • A back button appears in the input
  • You can go back to the previous group by pressing the ⌫ key

With fetched items

You can fetch items from an API and use them in the CommandPalette.

No data
<script setup lang="ts">
const searchTerm = ref('')

const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
  key: 'command-palette-users',
  transform: (data: { id: number, name: string, email: string }[]) => {
    return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
  },
  lazy: true,
  onRequestError({ request }) { console.warn('[fetch request error]', request) }
})

const groups = computed(() => [{
  id: 'users',
  label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
  items: users.value || []
}])
</script>

<template>
  <B24CommandPalette
    v-model:search-term="searchTerm"
    :loading="status === 'pending'"
    :groups="groups"
    class="flex-1 h-[320px]"
  />
</template>

With ignore filter

You can set the ignoreFilter field to true on a group to disable the internal search and use your own search logic.

Users
<script setup lang="ts">
import { refDebounced } from '@vueuse/core'

const searchTerm = ref('')
const searchTermDebounced = refDebounced(searchTerm, 200)

const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
  params: { q: searchTermDebounced },
  transform: (data: { id: number, name: string, email: string }[]) => {
    return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
  },
  lazy: true,
  onRequestError({ request }) { console.warn('[fetch request error]', request) }
})

const groups = computed(() => [{
  id: 'users',
  label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
  items: users.value || [],
  ignoreFilter: true
}])
</script>

<template>
  <B24CommandPalette
    v-model:search-term="searchTerm"
    :loading="status === 'pending'"
    :groups="groups"
    class="flex-1 h-[320px]"
  />
</template>
This example uses refDebounced to debounce the API calls.

With post-filtered items

You can use the postFilter field on a group to filter items after the search happened.

<script setup lang="ts">
const items = [
  {
    id: '/',
    label: 'Introduction',
    level: 1
  },
  {
    id: '/docs/getting-started/#whats-new-in-v3',
    label: 'What\'s new in v3?',
    level: 2
  },
  {
    id: '/docs/getting-started/#reka-ui',
    label: 'Reka UI',
    level: 3
  },
  {
    id: '/docs/getting-started/#tailwind-css-v4',
    label: 'Tailwind CSS v4',
    level: 3
  },
  {
    id: '/docs/getting-started/#tailwind-variants',
    label: 'Tailwind Variants',
    level: 3
  },
  {
    id: '/docs/getting-started/installation/',
    label: 'Installation',
    level: 1
  }
]

function postFilter(searchTerm: string, items: any[]) {
  // Filter only first level items if no searchTerm
  if (!searchTerm) {
    return items?.filter(item => item.level === 1)
  }

  return items
}
</script>

<template>
  <B24CommandPalette :groups="[{ id: 'files', items, postFilter }]" class="flex-1" />
</template>
Start typing to see items with higher level appear.

With custom fuse search

You can use the fuse prop to override the options of useFuse which defaults to:

{
  fuseOptions: {
    ignoreLocation: true,
    threshold: 0.1,
    keys: ['label', 'suffix']
  },
  resultLimit: 12,
  matchAllWhenSearchEmpty: true
}
The fuseOptions are the options of Fuse.js, the resultLimit is the maximum number of results to return and the matchAllWhenSearchEmpty is a boolean to match all items when the search term is empty.

You can for example set { fuseOptions: { includeMatches: true } } to highlight the search term in the items.

No data
<script setup lang="ts">
const { data: users } = await useFetch('https://jsonplaceholder.typicode.com/users', {
  key: 'command-palette-users',
  transform: (data: { id: number, name: string, email: string }[]) => {
    return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
  },
  lazy: true,
  onRequestError({ request }) { console.warn('[fetch request error]', request) }
})
</script>

<template>
  <B24CommandPalette
    :groups="[{ id: 'users', items: users || [] }]"
    :fuse="{ fuseOptions: { includeMatches: true } }"
    class="flex-1 h-[320px]"
  />
</template>

With virtualization

Use the virtualize prop to enable virtualization for large lists as a boolean or an object with options like { estimateSize: 32, overscan: 12 }.

When enabled, all groups are flattened into a single list due to a limitation of Reka UI.
<script setup lang="ts">
import type { CommandPaletteItem } from '@bitrix24/b24ui-nuxt'

const items: CommandPaletteItem[] = Array(1000)
  .fill(0)
  .map((_, value) => ({
    label: `item-${value}`,
    value
  }))

const groups = [
  {
    id: 'items',
    items
  }
]
</script>

<template>
  <B24CommandPalette
    virtualize
    :fuse="{ resultLimit: 1000 }"
    :groups="groups"
    class="flex-1 h-[320px]"
  />
</template>

Within a Popover

You can use the CommandPalette component inside a Popover's content.

<script setup lang="ts">
import TagIcon from '@bitrix24/b24icons-vue/outline/TagIcon'

const items = ref([
  {
    label: 'bug',
    value: 'bug',
    chip: {
      color: 'air-primary-alert' as const
    }
  },
  {
    label: 'feature',
    value: 'feature',
    chip: {
      color: 'air-primary-success' as const
    }
  },
  {
    label: 'enhancement',
    value: 'enhancement',
    chip: {
      color: 'air-primary' as const
    }
  }
])
const label = ref([])
</script>

<template>
  <B24Popover :content="{ side: 'right', align: 'start' }">
    <B24Button
      :icon="TagIcon"
      label="Select labels"
    />

    <template #content>
      <B24CommandPalette
        v-model="label"
        :multiple="true"
        placeholder="Search labels..."
        :groups="[{ id: 'labels', items }]"
        :b24ui="{ input: '[&>input]:h-8 [&>input]:text-sm' }"
      />
    </template>
  </B24Popover>
</template>

Within a Modal

You can use the CommandPalette component inside a Modal's content.

<script setup lang="ts">
import PersonSearchIcon from '@bitrix24/b24icons-vue/outline/PersonSearchIcon'

const searchTerm = ref('')

const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
  key: 'command-palette-users',
  params: { q: searchTerm },
  transform: (data: { id: number, name: string, email: string }[]) => {
    return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
  },
  lazy: true,
  onRequestError({ request }) { console.warn('[fetch request error]', request) }
})

const groups = computed(() => [{
  id: 'users',
  label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
  items: users.value || [],
  ignoreFilter: true
}])
</script>

<template>
  <B24Modal :b24ui="{ content: 'p-0 pt-0 pb-[10px]' }">
    <B24Button
      label="Search users..."
      :icon="PersonSearchIcon"
    />

    <template #content>
      <B24CommandPalette
        v-model:search-term="searchTerm"
        :loading="status === 'pending'"
        :groups="groups"
        placeholder="Search users..."
        class="h-[320px]"
      />
    </template>
  </B24Modal>
</template>

Listen open state

When using the close prop, you can listen to the update:open event when the button is clicked.

<script setup lang="ts">
import SearchIcon from '@bitrix24/b24icons-vue/outline/SearchIcon'

const open = ref(false)

const users = [
  {
    label: 'Assistant Name',
    suffix: 'assistant',
    to: 'https://github.com/bitrix24',
    target: '_blank',
    avatar: {
      src: '/b24ui/avatar/assistant.png'
    }
  },
  {
    label: 'Bitrix24',
    suffix: 'bitrix24',
    to: 'https://github.com/bitrix24',
    target: '_blank',
    avatar: {
      src: 'https://github.com/bitrix24.png'
    }
  },
  {
    label: 'Employee Name',
    suffix: 'employee',
    to: 'https://github.com/bitrix24',
    avatarSrc: '/b24ui/avatar/employee.png',
    target: '_blank',
    avatar: {
      src: '/b24ui/avatar/employee.png'
    }
  }
]
</script>

<template>
  <B24Modal v-model:open="open" :b24ui="{ content: 'p-0 pt-0 pb-[10px]' }">
    <B24Button
      label="Search users..."
      :icon="SearchIcon"
    />

    <template #content>
      <B24CommandPalette close :groups="[{ id: 'users', items: users }]" @update:open="open = $event" />
    </template>
  </B24Modal>
</template>
This can be useful when using the CommandPalette inside a Modal for example.

With footer slot

Use the #footer slot to add custom content at the bottom of the CommandPalette, such as keyboard shortcuts help or additional actions.

Recent
<script setup lang="ts">
import Bitrix24Icon from '@bitrix24/b24icons-vue/common-service/Bitrix24Icon'
import VueIcon from '@bitrix24/b24icons-vue/file-type/VueIcon'
import MarkdownIcon from '@bitrix24/b24icons-vue/file-type/MarkdownIcon'
import NodeIcon from '@bitrix24/b24icons-vue/file-type/NodeIcon'
import FileUploadIcon from '@bitrix24/b24icons-vue/main/FileUploadIcon'
import FolderPlusIcon from '@bitrix24/b24icons-vue/outline/FolderPlusIcon'
import CrmSearchIcon from '@bitrix24/b24icons-vue/crm/CrmSearchIcon'
import SettingsLIcon from '@bitrix24/b24icons-vue/outline/SettingsLIcon'

const groups = [
  {
    id: 'actions',
    items: [
      {
        label: 'Add new file',
        suffix: 'Create a new file in the current directory',
        icon: FileUploadIcon,
        kbds: ['meta', 'I']
      },
      {
        label: 'Add new folder',
        suffix: 'Create a new folder in the current directory',
        icon: FolderPlusIcon,
        kbds: ['meta', 'F']
      },
      {
        label: 'Search files',
        suffix: 'Search across all files in the project',
        icon: CrmSearchIcon,
        kbds: ['meta', 'P']
      },
      {
        label: 'Settings',
        suffix: 'Open application settings',
        icon: SettingsLIcon,
        kbds: ['meta', ',']
      }
    ]
  },
  {
    id: 'recent',
    label: 'Recent',
    items: [
      {
        label: 'project.vue',
        suffix: 'components/',
        icon: VueIcon
      },
      {
        label: 'readme.md',
        suffix: 'docs/',
        icon: MarkdownIcon
      },
      {
        label: 'package.json',
        suffix: 'root/',
        icon: NodeIcon
      }
    ]
  }
]
</script>

<template>
  <B24CommandPalette :groups="groups" class="flex-1 h-80">
    <template #footer>
      <div class="flex items-center justify-between gap-2">
        <Bitrix24Icon class="size-5 text-(--b24ui-typography-label-color) ml-1" />
        <div class="flex items-center gap-1">
          <B24Button label="Open Command" size="sm">
            <template #trailing>
              <B24Kbd value="enter" size="sm" />
            </template>
          </B24Button>
          <B24Separator orientation="vertical" class="h-4" />
          <B24Button label="Actions" size="sm">
            <template #trailing>
              <B24Kbd value="meta" size="sm" />
              <B24Kbd value="k" size="sm" />
            </template>
          </B24Button>
        </div>
      </div>
    </template>
  </B24CommandPalette>
</template>

With custom slot

Use the slot property to customize a specific item or group.

You will have access to the following slots:

  • #{{ item.slot }}
  • #{{ item.slot }}-leading
  • #{{ item.slot }}-label
  • #{{ item.slot }}-trailing
  • #{{ group.slot }}
  • #{{ group.slot }}-leading
  • #{{ group.slot }}-label
  • #{{ group.slot }}-trailing
Users
Assistant NameassistantBitrix24bitrix24Employee Nameemployee
<script setup lang="ts">
import UserMaskIcon from '@bitrix24/b24icons-vue/outline/UserMaskIcon'
import CreditDebitCardIcon from '@bitrix24/b24icons-vue/main/CreditDebitCardIcon'
import NotificationIcon from '@bitrix24/b24icons-vue/outline/NotificationIcon'
import LockLIcon from '@bitrix24/b24icons-vue/outline/LockLIcon'

const groups = [
  {
    id: 'settings',
    items: [
      {
        label: 'Profile',
        icon: UserMaskIcon,
        kbds: ['meta', 'P']
      },
      {
        label: 'Billing',
        icon: CreditDebitCardIcon,
        kbds: ['meta', 'B'],
        slot: 'billing' as const
      },
      {
        label: 'Notifications',
        icon: NotificationIcon
      },
      {
        label: 'Security',
        icon: LockLIcon
      }
    ]
  },
  {
    id: 'users',
    label: 'Users',
    slot: 'users' as const,
    items: [
      {
        label: 'Assistant Name',
        suffix: 'assistant',
        to: 'https://github.com/bitrix24',
        avatarSrc: '/b24ui/avatar/assistant.png',
        target: '_blank'
      },
      {
        label: 'Bitrix24',
        suffix: 'bitrix24',
        to: 'https://github.com/bitrix24',
        avatarSrc: 'https://github.com/bitrix24.png',
        target: '_blank'
      },
      {
        label: 'Employee Name',
        suffix: 'employee',
        to: 'https://github.com/bitrix24',
        avatarSrc: '/b24ui/avatar/employee.png',
        target: '_blank'
      }
    ]
  }
]
</script>

<template>
  <B24CommandPalette :groups="groups" class="flex-1 h-80">
    <template #users-leading="{ item }">
      <B24Avatar :src="item?.avatarSrc" size="2xs" />
    </template>

    <template #billing-label="{ item }">
      <span class="font-(--ui-font-weight-medium) text-(--ui-color-accent-main-primary)">{{ item.label }}</span>

      <B24Badge size="sm">
        50% off
      </B24Badge>
    </template>
  </B24CommandPalette>
</template>
You can also use the #item, #item-leading, #item-label and #item-trailing slots to customize all items.

API

Props

Prop Default Type
as'div'any

The element or component this component should render as.

iconicons.searchIconComponent

The icon displayed in the input.

trailingIconicons.searchIconComponent

The icon displayed on the right side of the input.

selectedIconicons.checkIconComponent

The icon displayed when an item is selected.

childrenIconicons.chevronRightIconComponent

The icon displayed when an item has children.

placeholdert('commandPalette.placeholder') string

The placeholder text for the input.

autofocustrueboolean

Automatically focus the input when component is mounted.

closefalseboolean | Omit<ButtonProps, LinkPropsKeys>

Display a close button in the input (useful when inside a Modal for example). { size: 'sm', color: 'air-tertiary-no-accent' }

closeIconicons.closeIconComponent

The icon displayed in the close button.

backtrueboolean | Omit<ButtonProps, LinkPropsKeys>

Display a button to navigate back in history. { size: 'sm', color: 'air-selection' }

backIconicons.arrowLeftIconComponent

The icon displayed in the back button.

groups CommandPaletteGroup<CommandPaletteItem>[]
fuse{ fuseOptions: { ignoreLocation: true, threshold: 0.1, keys: ['label', 'suffix'] }, resultLimit: 12, matchAllWhenSearchEmpty: true } n<CommandPaletteItem>

Options for useFuse.

virtualizefalseboolean | { overscan?: number ; estimateSize?: number | undefined; } | undefined

Enable virtualization for large lists. Note: when enabled, all groups are flattened into a single list due to a limitation of Reka UI (https://github.com/unovue/reka-ui/issues/1885).

  • overscan?: number

    Number of items rendered outside the visible area Defaults to 12.

  • estimateSize?: number

    Estimated size (in px) of each item Defaults to 32.

labelKey'label' string | number

The key used to get the label from the item.

descriptionKey'description' string | number

The key used to get the description from the item.

preserveGroupOrderfalseboolean

Whether to preserve the order of groups as defined in the groups prop when filtering. When false, groups will appear based on item matches.

multipleboolean

Whether multiple options can be selected or not.

disabledboolean

When true, prevents the user from interacting with listbox

modelValue'' null | string | number | bigint | Record<string, any> | AcceptableValue[]

The controlled value of the listbox. Can be binded with with v-model.

defaultValue null | string | number | bigint | Record<string, any> | AcceptableValue[]

The value of the listbox when initially rendered. Use when you do not need to control the state of the Listbox

highlightOnHovertrueboolean

When true, hover over item will trigger highlight

selectionBehavior'toggle' "replace" | "toggle"

How multiple selection should behave in the collection.

loadingboolean

When true, the loading icon will be displayed.

searchTerm'' string
b24ui { root?: ClassNameValue; input?: ClassNameValue; close?: ClassNameValue; back?: ClassNameValue; content?: ClassNameValue; footer?: ClassNameValue; viewport?: ClassNameValue; group?: ClassNameValue; empty?: ClassNameValue; label?: ClassNameValue; item?: ClassNameValue; itemLeadingIcon?: ClassNameValue; itemLeadingAvatar?: ClassNameValue; itemLeadingAvatarSize?: ClassNameValue; itemLeadingChip?: ClassNameValue; itemLeadingChipSize?: ClassNameValue; itemTrailing?: ClassNameValue; itemTrailingIcon?: ClassNameValue; itemTrailingHighlightedIcon?: ClassNameValue; itemTrailingKbds?: ClassNameValue; itemTrailingKbdsSize?: ClassNameValue; itemWrapper?: ClassNameValue; itemLabel?: ClassNameValue; itemDescription?: ClassNameValue; itemLabelBase?: ClassNameValue; itemLabelPrefix?: ClassNameValue; itemLabelSuffix?: ClassNameValue; }

Slots

Slot Type
empty{ searchTerm?: string | undefined; }
footer{ b24ui: object; }
back{ b24ui: object; }
close{ b24ui: object; }
item{ item: CommandPaletteItem; index: number; b24ui: object; }
item-leading{ item: CommandPaletteItem; index: number; b24ui: object; }
item-label{ item: CommandPaletteItem; index: number; b24ui: object; }
item-description{ item: CommandPaletteItem; index: number; b24ui: object; }
item-trailing{ item: CommandPaletteItem; index: number; b24ui: object; }

Emits

Event Type
update:modelValue[value: CommandPaletteItem]
highlight[payload: { ref: HTMLElement; value: CommandPaletteItem; } | undefined]
entryFocus[event: CustomEvent<any>]
leave[event: Event]
update:open[value: boolean]
update:searchTerm[value: string]

Theme

app.config.ts
export default defineAppConfig({
  b24ui: {
    commandPalette: {
      slots: {
        root: 'flex flex-col min-h-0 min-w-0 divide-y divide-(--ui-color-design-tinted-na-stroke)',
        input: 'ps-[40px] pe-[44px]',
        close: '',
        back: 'p-0',
        content: 'relative overflow-hidden flex flex-col',
        footer: 'p-1',
        viewport: 'relative scroll-py-1 overflow-y-auto scrollbar-thin flex-1 focus:outline-none',
        group: 'p-1 isolate',
        empty: 'py-6 text-center text-(length:--ui-font-size-sm) text-(--b24ui-typography-description-color)',
        label: 'p-[6px] text-(length:--ui-font-size-xs) text-(--b24ui-typography-label-color)',
        item: 'group relative w-full flex items-center gap-[6px] p-[6px] text-(length:--ui-font-size-sm) select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-(--ui-border-radius-sm) data-disabled:cursor-not-allowed data-disabled:opacity-30',
        itemLeadingIcon: 'shrink-0 size-[20px]',
        itemLeadingAvatar: 'shrink-0',
        itemLeadingAvatarSize: '2xs',
        itemLeadingChip: 'shrink-0 size-[20px]',
        itemLeadingChipSize: 'md',
        itemTrailing: 'ms-auto inline-flex gap-[6px] items-center',
        itemTrailingIcon: 'shrink-0 size-[20px] text-(--ui-color-accent-main-primary)',
        itemTrailingHighlightedIcon: 'shrink-0 size-[20px] text-(--b24ui-typography-label-color) hidden group-data-highlighted:inline-flex',
        itemTrailingKbds: 'hidden lg:inline-flex items-center shrink-0 gap-0.5',
        itemTrailingKbdsSize: 'md',
        itemWrapper: 'flex-1 flex flex-col text-start min-w-0 overflow-hidden',
        itemLabel: 'truncate space-x-1 text-(--b24ui-typography-label-color)',
        itemDescription: 'truncate -mt-[6px] text-(--b24ui-typography-description-color) text-(length:--ui-font-size-sm)',
        itemLabelBase: 'text-(--b24ui-typography-label-color) [&>mark]:text-(--ui-color-black-base) [&>mark]:bg-(--ui-color-collab-accent-less-1)',
        itemLabelPrefix: 'text-(--b24ui-typography-legend-color)',
        itemLabelSuffix: 'truncate text-(--b24ui-typography-description-color) [&>mark]:text-(--ui-color-black-base) [&>mark]:bg-(--ui-color-collab-accent-less-1)'
      },
      variants: {
        virtualize: {
          true: {
            viewport: 'p-1 isolate'
          },
          false: {
            viewport: ''
          }
        },
        active: {
          true: {
            item: 'text-(--b24ui-typography-label-color) before:bg-(--ui-color-base-8)',
            itemLeadingIcon: 'text-(--b24ui-typography-legend-color)'
          },
          false: {
            item: 'text-(--b24ui-typography-legend-color) data-highlighted:not-data-disabled:text-(--b24ui-typography-legend-color) data-highlighted:not-data-disabled:before:bg-(--ui-color-bg-content-secondary) transition-colors before:transition-colors',
            itemLeadingIcon: 'text-(--b24ui-typography-legend-color) group-data-highlighted:not-group-data-disabled:text-(--b24ui-typography-legend-color) transition-colors'
          }
        },
        loading: {
          true: {
            itemLeadingIcon: 'animate-spin'
          }
        }
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import bitrix24UIPluginVite from '@bitrix24/b24ui-nuxt/vite'

export default defineConfig({
  plugins: [
    vue(),
    bitrix24UIPluginVite({
      b24ui: {
        commandPalette: {
          slots: {
            root: 'flex flex-col min-h-0 min-w-0 divide-y divide-(--ui-color-design-tinted-na-stroke)',
            input: 'ps-[40px] pe-[44px]',
            close: '',
            back: 'p-0',
            content: 'relative overflow-hidden flex flex-col',
            footer: 'p-1',
            viewport: 'relative scroll-py-1 overflow-y-auto scrollbar-thin flex-1 focus:outline-none',
            group: 'p-1 isolate',
            empty: 'py-6 text-center text-(length:--ui-font-size-sm) text-(--b24ui-typography-description-color)',
            label: 'p-[6px] text-(length:--ui-font-size-xs) text-(--b24ui-typography-label-color)',
            item: 'group relative w-full flex items-center gap-[6px] p-[6px] text-(length:--ui-font-size-sm) select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-(--ui-border-radius-sm) data-disabled:cursor-not-allowed data-disabled:opacity-30',
            itemLeadingIcon: 'shrink-0 size-[20px]',
            itemLeadingAvatar: 'shrink-0',
            itemLeadingAvatarSize: '2xs',
            itemLeadingChip: 'shrink-0 size-[20px]',
            itemLeadingChipSize: 'md',
            itemTrailing: 'ms-auto inline-flex gap-[6px] items-center',
            itemTrailingIcon: 'shrink-0 size-[20px] text-(--ui-color-accent-main-primary)',
            itemTrailingHighlightedIcon: 'shrink-0 size-[20px] text-(--b24ui-typography-label-color) hidden group-data-highlighted:inline-flex',
            itemTrailingKbds: 'hidden lg:inline-flex items-center shrink-0 gap-0.5',
            itemTrailingKbdsSize: 'md',
            itemWrapper: 'flex-1 flex flex-col text-start min-w-0 overflow-hidden',
            itemLabel: 'truncate space-x-1 text-(--b24ui-typography-label-color)',
            itemDescription: 'truncate -mt-[6px] text-(--b24ui-typography-description-color) text-(length:--ui-font-size-sm)',
            itemLabelBase: 'text-(--b24ui-typography-label-color) [&>mark]:text-(--ui-color-black-base) [&>mark]:bg-(--ui-color-collab-accent-less-1)',
            itemLabelPrefix: 'text-(--b24ui-typography-legend-color)',
            itemLabelSuffix: 'truncate text-(--b24ui-typography-description-color) [&>mark]:text-(--ui-color-black-base) [&>mark]:bg-(--ui-color-collab-accent-less-1)'
          },
          variants: {
            virtualize: {
              true: {
                viewport: 'p-1 isolate'
              },
              false: {
                viewport: ''
              }
            },
            active: {
              true: {
                item: 'text-(--b24ui-typography-label-color) before:bg-(--ui-color-base-8)',
                itemLeadingIcon: 'text-(--b24ui-typography-legend-color)'
              },
              false: {
                item: 'text-(--b24ui-typography-legend-color) data-highlighted:not-data-disabled:text-(--b24ui-typography-legend-color) data-highlighted:not-data-disabled:before:bg-(--ui-color-bg-content-secondary) transition-colors before:transition-colors',
                itemLeadingIcon: 'text-(--b24ui-typography-legend-color) group-data-highlighted:not-group-data-disabled:text-(--b24ui-typography-legend-color) transition-colors'
              }
            },
            loading: {
              true: {
                itemLeadingIcon: 'animate-spin'
              }
            }
          }
        }
      }
    })
  ]
})

Breadcrumb

A breadcrumb navigation component.

Link

A wrapper around <NuxtLink> with extra props.

On this page

  • Usage
    • Groups
    • Multiple
    • Placeholder
    • Icon
    • Selected Icon
    • Trailing Icon
    • Loading
    • Close
    • Close Icon
    • Back
    • Back Icon
    • Disabled
  • Examples
    • Control selected item(s)
    • Control search term
    • With children in items
    • With fetched items
    • With ignore filter
    • With post-filtered items
    • With custom fuse search
    • With virtualization
    • Within a Popover
    • Within a Modal
    • Listen open state
    • With footer slot
    • With custom slot
  • API
    • Props
    • Slots
    • Emits
  • Theme
Releases
Published under MIT License.

Copyright © 2024-present Bitrix24