https://github.com/yvng-jie/vue-cmdk
⌘K — Fast, composable, unstyled command palette for Vue 3
https://github.com/yvng-jie/vue-cmdk
cmdk command-palette keyboard menu vue vue-component vue3
Last synced: 20 days ago
JSON representation
⌘K — Fast, composable, unstyled command palette for Vue 3
- Host: GitHub
- URL: https://github.com/yvng-jie/vue-cmdk
- Owner: yvng-jie
- License: mit
- Created: 2026-05-30T08:05:59.000Z (21 days ago)
- Default Branch: main
- Last Pushed: 2026-05-30T09:50:58.000Z (20 days ago)
- Last Synced: 2026-05-30T10:22:15.154Z (20 days ago)
- Topics: cmdk, command-palette, keyboard, menu, vue, vue-component, vue3
- Language: Vue
- Homepage: https://yvng-jie.github.io/vue-cmdk/
- Size: 55.7 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
⌘K — vue-cmdk
A fast, composable, unstyled command palette for Vue 3.
Press ⌘K and take control.
---
## 🧠 Inspiration
This project is heavily inspired by two great projects:
| Project | Author | Description |
| --- | --- | --- |
| [`vue-command-palette`](https://github.com/xiaoluoboding/vue-command-palette) | [@xiaoluoboding](https://github.com/xiaoluoboding) | The first composable command palette for Vue, with 590 ★ |
| [`cmdk`](https://github.com/pacocoursey/cmdk) | [@pacocoursey](https://github.com/pacocoursey) | Fast, unstyled command menu React component (10k+ ★) |
### Why another one?
[`vue-command-palette`](https://github.com/xiaoluoboding/vue-command-palette) pioneered the ⌘K experience for Vue. Big thanks to [@xiaoluoboding](https://github.com/xiaoluoboding) for the original idea and work 🙌
However, the project has been **inactive since September 2023** — 9 issues remain unanswered, no dependencies have been updated in years, and the bundle size is 28 kB. The Vue ecosystem deserves a **well-maintained, lightweight alternative** that keeps up with modern standards.
**vue-cmdk** is built to fill that gap: same compound component API, zero dependencies, half the size, with full TypeScript support and a commitment to ongoing maintenance.
### vs vue-command-palette (legacy reference)
| Feature | `vue-command-palette` | `vue-cmdk` |
| ---------------- | :----------------------: | :-----------: |
| 📦 Bundle (min) | 28.2 kB | **11.8 kB** |
| 📦 Bundle (gzip) | 9.6 kB | **3.4 kB** |
| 🔍 Search | fuse.js (extra dep) | **Built-in** |
| 📋 TypeScript | Partial | **Full** |
| 🔄 Async items | ❌ | ✅ |
| 🧩 Custom filter | ❌ | ✅ |
| 🔒 Focus trap | ❌ | ✅ |
| 🧹 Dependencies | 3 (fuse.js, nanoid, ...) | **0** |
| 🔧 Maintenance | ❌ Inactive since 2023 | ✅ **Active** |
---
## ✨ Features
- **🧩 Compound component API** — ``, ``, ``, etc.
- **💄 Unstyled** — Bring your own CSS, zero opinions, full design control
- **🔍 Built-in search** — Fast case-insensitive filtering with keyword matching
- **⌨️ Keyboard-first** — Arrow keys, Enter, Escape — all built-in, no config needed
- **📦 Tiny** — 3.4 kB gzipped, **zero runtime dependencies** (peer: `vue` only)
- **🎯 TypeScript** — Full type inference and declaration files
- **🔄 Dynamic items** — Pass items as a reactive array, swap anytime
- **🛠 Custom filter** — Provide your own filter function
- **♿ Accessible** — ARIA attributes, focus trap, `aria-live` region
## 📊 Comparison with React cmdk
`vue-cmdk` is a Vue 3 port inspired by the excellent [`cmdk`](https://github.com/pacocoursey/cmdk) (React). Below is the current feature parity status:
| # | Feature | React `cmdk` | `vue-cmdk` | Status |
| --- | -------------------------------------------- | :-------------: | :--------------: | :--------: |
| 1 | `Command` root `value` / `onValueChange` | ✅ | ❌ | 📋 Planned |
| 2 | `Command` root `shouldFilter` | ✅ | ❌ | 📋 Planned |
| 3 | `Command` root `loop` | ✅ | ❌ | 📋 Planned |
| 4 | `Command` root `label` (aria-label) | ✅ | ❌ | 📋 Planned |
| 5 | `Command.Dialog` `open` / `onOpenChange` | ✅ `open` | ✅ `visible` | ✅ |
| 6 | `Command.Dialog` `container` (portal target) | ✅ | ❌ | 💡 Future |
| 7 | `Command.Input` `value` / `onValueChange` | ✅ | ✅ `searchQuery` | ✅ |
| 8 | `Command.Item` `forceMount` | ✅ | ❌ | 📋 Planned |
| 9 | `Command.Item` `keywords` | ✅ | ✅ | ✅ |
| 10 | `Command.Item` `onSelect` | ✅ | ✅ | ✅ |
| 11 | `Command.Item` auto value from textContent | ✅ | ❌ | 📋 Planned |
| 12 | `Command.Group` `forceMount` | ✅ | ❌ | 📋 Planned |
| 13 | `Command.Group` `heading` | ✅ | ✅ | ✅ |
| 14 | `Command.Separator` `alwaysRender` | ✅ | ❌ | 💡 Future |
| 15 | `Command.Empty` | ✅ | ✅ | ✅ |
| 16 | `Command.Loading` | ✅ | ✅ | ✅ |
| 17 | `useCommandState()` state selector | ✅ | ❌ | 📋 Planned |
| 18 | **Nested items / Pages** | ✅ (pattern) | ❌ | 📋 Planned |
| 19 | **Built-in search / filtering** | ✅ | ✅ | ✅ |
| 20 | **Custom filter function** | ✅ (rank-based) | ✅ (item-based) | ✅ |
| 21 | **Global shortcut listener** | ❌ (manual) | ✅ (built-in) | ✅ Bonus |
| 22 | **Keyboard navigation** | ✅ | ✅ | ✅ |
| 23 | **Focus trap** | ✅ (Radix) | ✅ (custom) | ✅ |
| 24 | **Zero dependencies** | ❌ (Radix UI) | ✅ (0 deps) | ✅ Better |
| 25 | **TypeScript** | ✅ | ✅ | ✅ |
| 26 | **Unstyled** | ✅ | ✅ | ✅ |
| 27 | **Bundle size (gzip)** | ~7 kB | **3.4 kB** | ✅ Smaller |
> **Legend** — ✅ Done · 📋 High/Medium priority · 💡 Low priority / Nice to have
## 🚀 Install
```bash
npm install vue-command-kit
```
## Quick Start
### Simple — items prop
```vue
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)
}
Open (⌘K)
```
### Advanced — custom slot content
```vue
```
### With custom filter
```vue
import { Command } from 'vue-command-kit'
function myFilter(items: CommandItemData[], query: string) {
// Return filtered items, or null to use default filter
return items.filter((item) => item.label?.includes(query))
}
```
## 📖 API
### `` Props
| Prop | Type | Default | Description |
| --------------- | ------------------- | --------------------- | ---------------------------- |
| `visible` | `boolean` | `false` | Controlled open state |
| `items` | `CommandItemData[]` | `[]` | Items to display |
| `placeholder` | `string` | `'Type a command...'` | 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 |
### `` Events
| Event | Payload | Description |
| ---------------- | ----------------- | -------------------------------- |
| `update:visible` | `boolean` | Emitted when visibility changes |
| `select` | `CommandItemData` | Emitted when an item is selected |
### Components
| Component | Description |
| --------------------- | ---------------------------------------------- |
| `` | Modal dialog with mask, transition, focus trap |
| `` | Inline command menu (non-modal) |
| `` | Search input with keyboard navigation |
| `` | Scrollable list rendering `groupedItems` |
| `` | Group of items with heading |
| `` | Single selectable command item |
| `` | Shown when no results match |
| `` | Visual separator |
| `` | Loading indicator |
### `CommandItemData`
```ts
interface CommandItemData {
value: string
label?: string
keywords?: string[]
shortcut?: string
group?: string
disabled?: boolean
icon?: Component
onSelect?: (item: CommandItemData) => void
}
```
### `useCommandMenu()` Composable
```ts
import { useCommandMenu } from 'vue-command-kit'
import type { UseCommandMenuReturn, FilterFn } from 'vue-command-kit'
const menu: UseCommandMenuReturn = useCommandMenu(customFilter?)
menu.items.value = [...]
menu.open()
menu.close()
menu.toggle()
```
### Keyboard
| Key | Action |
| -------- | -------------------------------------- |
| `↑` `↓` | Navigate items (wraps around) |
| `Enter` | Select current item |
| `Escape` | Close dialog |
| `Tab` | Moves focus within dialog (focus trap) |
## 🤝 Contributing
```bash
# dev
pnpm dev
# type check
pnpm typecheck
# build
pnpm build
```
PRs and issues are welcome!
## 📄 License
MIT © [yvng-jie](https://github.com/yvng-jie)
---
Made with ❤️ for the Vue community. Thanks to @xiaoluoboding for the original vue-command-palette.