A fast, composable, unstyled command palette for Vue 3.
- Compound component API — Declarative composition with
<Command.Dialog>,<Command.Input>,<Command.Item>, and more - Built-in search — Case-insensitive filtering with keyword matching and result highlighting
- Force render —
forceMountprop keeps items in the DOM when filtered out (for animations) - Keyboard-first — Arrow key navigation, Enter to select, Escape to close, global shortcuts
- Unstyled — Zero CSS opinions; bring your own styles
- TypeScript — Full type inference, exported types, and declaration files
- Zero runtime dependencies — Peer dependency only on
vue ^3.4.0
Midnight theme · Nested pages · Search filtering · Disabled items
npm install vue-command-kit<script setup lang="ts">
import { ref } from 'vue'
import { Command } from 'vue-command-kit'
import type { CommandItemData } from 'vue-command-kit'
const visible = ref(false)
const items: CommandItemData[] = [
{ value: 'settings', label: 'Open settings', shortcut: '⌘,' },
{ value: 'home', label: 'Go to home', shortcut: '⌘H' },
]
function onSelect(item: CommandItemData) {
console.log('selected:', item.value)
}
</script>
<template>
<button @click="visible = true">Open (⌘K)</button>
<Command.Dialog
:visible="visible"
:items="items"
@update:visible="visible = $event"
@select="onSelect"
/>
</template><Command.Dialog :visible="visible" @update:visible="visible = $event">
<template #header>
<Command.Input placeholder="Search..." />
</template>
<template #body>
<Command.List>
<Command.Group heading="Favorites">
<Command.Item value="home" label="Home" />
</Command.Group>
</Command.List>
</template>
</Command.Dialog>| Component | Description |
|---|---|
<Command.Dialog> |
Modal dialog with mask, transition, focus trap |
<Command.Menu> |
Inline command menu (non-modal) with slots |
<Command.Input> |
Search input with keyboard navigation |
<Command.List> |
Scrollable list rendering grouped items |
<Command.Group> |
Group of items with heading |
<Command.Item> |
Single selectable command item |
<Command.Separator> |
Visual separator between groups |
<Command.Empty> |
Shown when no results match |
<Command.Loading> |
Loading indicator |
| Prop | Type | Default | Description |
|---|---|---|---|
visible |
boolean |
false |
Controlled open state |
items |
CommandItemData[] |
[] |
Items to display |
searchQuery |
string |
— | Search query (v-model:searchQuery) |
value |
string |
— | Selected item value (v-model:value) |
placeholder |
string |
'Type a command or search...' |
Input placeholder |
filter |
FilterFn |
— | Custom filter function |
loading |
boolean |
false |
Show loading state |
autoFocus |
boolean |
true |
Auto-focus input on open |
closeOnSelect |
boolean |
true |
Close dialog after selection |
shouldFilter |
boolean |
true |
When false, skip built-in filtering |
loop |
boolean |
true |
When false, keyboard nav stops at boundaries |
transitionName |
string |
'cmdk-dialog' |
Custom transition name for the dialog overlay |
label |
string |
'Command menu' |
Accessible name for the dialog and combobox |
container |
string | HTMLElement |
'body' |
Teleport target for the dialog |
| Event | Payload | Description |
|---|---|---|
update:visible |
boolean |
Emitted when visibility changes |
update:searchQuery |
string |
Emitted when search query changes |
update:value |
string |
Emitted when an item is selected |
select |
CommandItemData |
Emitted when an item is selected |
The demo app is covered with Playwright end-to-end tests for the main command palette flows:
- open and close behavior
- keyboard navigation and selection
- search filtering, empty states, loading states, and disabled items
- theme switching through both buttons and command selection
- accessibility contracts such as
aria-activedescendant,aria-selected, focus trap, and automated dialog scanning
Run them locally with:
pnpm test:e2einterface CommandItemData {
value: string
label?: string
keywords?: string[]
shortcut?: string
group?: string
disabled?: boolean
forceMount?: boolean
icon?: Component | VNode | (() => VNode)
onSelect?: (item: CommandItemData) => void
/** Sub-page items. Selecting this item opens a sub-menu instead of closing */
children?: CommandItemData[]
}The composable provides programmatic control outside of Command.Dialog / Command.Menu.
import { useCommandMenu } from 'vue-command-kit'
const menu = useCommandMenu()
menu.items.value = [...]
menu.open()
menu.close()
menu.toggle()| Return | Type | Description |
|---|---|---|
visible |
Ref<boolean> |
Open state |
searchQuery |
Ref<string> |
Current search query |
activeIndex |
Ref<number> |
Currently highlighted item index |
items |
Ref<CommandItemData[]> |
Raw item list |
filteredItems |
ComputedRef<...> |
Items after filtering |
groupedItems |
ComputedRef<...> |
Filtered items grouped by group field |
canGoBack |
ComputedRef<boolean> |
Whether there is a parent sub-page |
pageTitle |
ComputedRef<string> |
Title of the current sub-page |
open() |
() => void |
Open the menu |
close() |
() => void |
Close and reset search |
toggle() |
() => void |
Toggle open state |
selectNext() |
() => void |
Move active index down |
selectPrev() |
() => void |
Move active index up |
selectCurrent() |
() => void |
Select currently active item |
pushPage() |
(subItems, title) => void |
Navigate to a sub-page |
goBack() |
() => void |
Return to the parent page |
| Format | Size |
|---|---|
| ESM | 21.2 kB |
| UMD | 17.2 kB |
| Gzipped | 6.2 kB |
MIT © yvng-jie
