An open API service indexing awesome lists of open source software.

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

Awesome Lists containing this project

README

          


npm
vue
license
size

⌘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.