npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

cmdk-vue

v1.0.0

Published

Fast, composable, unstyled command menu for Vue 3. A faithful port of cmdk by Paco Coursey.

Downloads

143

Readme

⌘K for Vue 3

A Vue 3 port of cmdk — the composable command menu component.

Original authors: Paco Coursey and contributors. The core architecture, filtering algorithm, accessibility design, and all fundamental ideas in this library are entirely their work. Please star the original.

This fork was created by Krushn Dayshmookh with the assistance of Claude (Anthropic AI). The goal is a line-for-line faithful Vue 3 port — minimal changes, maximum parity.


⌘K is a command menu component that can also be used as an accessible combobox. You render items, it filters and sorts them automatically. Supports a fully composable API How?, so you can wrap items in other components or even as static JSX.

Install

pnpm install cmdk-vue

Use

<script setup>
import { Command } from 'cmdk-vue'
</script>

<template>
  <Command label="Command Menu">
    <Command.Input />
    <Command.List>
      <Command.Empty>No results found.</Command.Empty>

      <Command.Group heading="Letters">
        <Command.Item>a</Command.Item>
        <Command.Item>b</Command.Item>
        <Command.Separator />
        <Command.Item>c</Command.Item>
      </Command.Group>

      <Command.Item>Apple</Command.Item>
    </Command.List>
  </Command>
</template>

Or in a dialog:

<script setup>
import { ref } from 'vue'
import { Command } from 'cmdk-vue'

const open = ref(false)

function onKeydown(e) {
  if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
    e.preventDefault()
    open.value = !open.value
  }
}

document.addEventListener('keydown', onKeydown)
</script>

<template>
  <Command.Dialog v-model:open="open" label="Global Command Menu">
    <Command.Input />
    <Command.List>
      <Command.Empty>No results found.</Command.Empty>

      <Command.Group heading="Letters">
        <Command.Item>a</Command.Item>
        <Command.Item>b</Command.Item>
        <Command.Separator />
        <Command.Item>c</Command.Item>
      </Command.Group>

      <Command.Item>Apple</Command.Item>
    </Command.List>
  </Command.Dialog>
</template>

Parts and styling

All parts forward attrs to an appropriate element. Each part has a specific data-attribute (starting with cmdk-) that can be used for styling.

Command [cmdk-root]

Render this to show the command menu inline, or use Dialog to render in an elevated context. Can be controlled with the value and onValueChange props.

Note

Values are always trimmed with the trim() method.

<script setup>
const value = ref('apple')
</script>

<template>
  <Command v-model:value="value">
    <Command.Input />
    <Command.List>
      <Command.Item>Orange</Command.Item>
      <Command.Item>Apple</Command.Item>
    </Command.List>
  </Command>
</template>

You can provide a custom filter function that is called to rank each item. Note that the value will be trimmed.

<Command :filter="(value, search) => (value.includes(search) ? 1 : 0)" />

A third argument, keywords, can also be provided to the filter function. Keywords act as aliases for the item value, and can also affect the rank of the item. Keywords are trimmed.

<Command
  :filter="
    (value, search, keywords) => {
      const extendValue = value + ' ' + keywords.join(' ')
      return extendValue.includes(search) ? 1 : 0
    }
  "
/>

Or disable filtering and sorting entirely:

<Command :should-filter="false">
  <Command.List>
    <Command.Item v-for="item in filteredItems" :key="item" :value="item">
      {{ item }}
    </Command.Item>
  </Command.List>
</Command>

You can make the arrow keys wrap around the list by setting the loop prop:

<Command loop />

Dialog [cmdk-dialog] [cmdk-overlay]

Props are forwarded to Command. Composes Reka UI's Dialog component. The overlay is always rendered. Can be controlled with v-model:open.

<Command.Dialog v-model:open="open">
  ...
</Command.Dialog>

You can provide a container prop to specify which element the Dialog should portal into (defaults to body).

Input [cmdk-input]

All attrs are forwarded to the underlying input element. Can be controlled with v-model.

<Command.Input v-model="search" />

List [cmdk-list]

Contains items and groups. Animate height using the --cmdk-list-height CSS variable.

[cmdk-list] {
  min-height: 300px;
  height: var(--cmdk-list-height);
  max-height: 500px;
  transition: height 100ms ease;
}

To scroll item into view earlier near the edges of the viewport, use scroll-padding:

[cmdk-list] {
  scroll-padding-block-start: 8px;
  scroll-padding-block-end: 8px;
}

Item [cmdk-item] [data-disabled?] [data-selected?]

Item that becomes active on pointer enter. You should provide a unique value for each item, but it will be automatically inferred from the .textContent.

<Command.Item @select="(value) => console.log('Selected', value)">
  Apple
</Command.Item>

You can also provide a keywords prop to help with filtering:

<Command.Item :keywords="['fruit', 'apple']">Apple</Command.Item>

You can force an item to always render, regardless of filtering, by passing the forceMount prop.

Group [cmdk-group] [hidden?]

Groups items together with the given heading ([cmdk-group-heading]).

<Command.Group heading="Fruit">
  <Command.Item>Apple</Command.Item>
</Command.Group>

Groups will not unmount from the DOM, rather the hidden attribute is applied to hide it from view.

You can force a group to always render, regardless of filtering, by passing the forceMount prop.

Separator [cmdk-separator]

Visible when the search query is empty or alwaysRender is true, hidden otherwise.

Empty [cmdk-empty]

Automatically renders when there are no results for the search query.

Loading [cmdk-loading]

You should conditionally render this with progress while loading asynchronous items.

<Command.List>
  <Command.Loading v-if="loading">Hang on…</Command.Loading>
</Command.List>

useCommandState(state => state.selectedField)

Composable that returns a computed ref of a slice of command menu state. Pass a selector function. This is provided for advanced use cases and should not be commonly used.

<script setup>
import { useCommandState } from 'cmdk-vue'
const search = useCommandState((state) => state.search)
</script>

<template>
  <Command.Empty>No results found for "{{ search }}".</Command.Empty>
</template>

Examples

Nested items

<script setup>
const search = ref('')
const pages = ref([])
const page = computed(() => pages.value[pages.value.length - 1])

function onKeydown(e) {
  if (e.key === 'Escape' || (e.key === 'Backspace' && !search.value)) {
    e.preventDefault()
    pages.value = pages.value.slice(0, -1)
  }
}
</script>

<template>
  <Command @keydown="onKeydown">
    <Command.Input v-model="search" />
    <Command.List>
      <template v-if="!page">
        <Command.Item @select="pages = [...pages, 'projects']">Search projects…</Command.Item>
        <Command.Item @select="pages = [...pages, 'teams']">Join a team…</Command.Item>
      </template>
      <template v-if="page === 'projects'">
        <Command.Item>Project A</Command.Item>
        <Command.Item>Project B</Command.Item>
      </template>
      <template v-if="page === 'teams'">
        <Command.Item>Team 1</Command.Item>
        <Command.Item>Team 2</Command.Item>
      </template>
    </Command.List>
  </Command>
</template>

Show sub-items when searching

<script setup>
import { useCommandState } from 'cmdk-vue'

const SubItem = defineComponent({
  setup(props, { slots }) {
    const search = useCommandState((state) => state.search)
    return () => (search.value ? h(CommandItem, props, slots) : null)
  },
})
</script>

Asynchronous results

<script setup>
const loading = ref(false)
const items = ref([])

onMounted(async () => {
  loading.value = true
  items.value = await api.get('/dictionary')
  loading.value = false
})
</script>

<template>
  <Command>
    <Command.Input />
    <Command.List>
      <Command.Loading v-if="loading">Fetching words…</Command.Loading>
      <Command.Item v-for="item in items" :key="item" :value="item">
        {{ item }}
      </Command.Item>
    </Command.List>
  </Command>
</template>

Drop in stylesheets

See website/styles/cmdk for example stylesheets.

FAQ

Accessible? Yes. Labeling, aria attributes, and DOM ordering tested with Voice Over and Chrome DevTools.

Virtualization? No. Good performance up to 2,000–3,000 items.

Filter/sort items manually? Yes. Pass :should-filter="false" to Command.

Vue 3? Yes, required. Uses Vue 3 reactivity and composables.

Unstyled? Yes, use the listed CSS selectors.

React strict mode equivalent? Not applicable — Vue has no equivalent concern.

History

The original ⌘K was written in 2019 by Paco (@pacocoursey). Used for the Vercel command menu and autocomplete by Rauno (@raunofreiberg) in 2020. Re-written in 2022 with a simpler and more performant approach. Ideas and help from Shu (@shuding_).

This Vue 3 port was created in 2026 by Krushn Dayshmookh with AI assistance (Claude, Anthropic). The architecture, scoring algorithm, and all core ideas remain those of the original authors.

Testing

pnpm install
pnpm playwright install
pnpm build
pnpm test