Skip to content

Guide · Open source

How to use cmdk: build a React command menu

Updated · written with AI from the project's own documentation and checked against our catalogue data

cmdk is an open-source React component for building a command menu, the searchable popup that opens with Cmd+K or Ctrl+K. You render a list of items and it filters and sorts them as the user types. This guide shows you how to install it, build an inline menu and a keyboard-triggered dialog, and customise filtering and styling.

cmdkdip/cmdk · ★ 13.1k · MIT

Quick start

  1. 1Install the package. Run pnpm install cmdk in your React project. You need Node.js and a package manager that works with npm.
  2. 2Import Command. Import the Command component from cmdk in the file where you want the menu.
  3. 3Add the input and list. Place Command.Input and Command.List inside Command, then add Command.Item entries for each option.
  4. 4Add a keyboard toggle. Listen for keydown events, check for k with metaKey or ctrlKey, and toggle the open state.
  5. 5Wrap in Command.Dialog. Replace the outer Command with Command.Dialog and pass open and onOpenChange from your state.
  6. 6Style the parts. Target the cmdk- data-attributes in your CSS to design the menu, since the component ships unstyled.

Install cmdk with a package manager

cmdk is distributed as a package and is installed with pnpm or another npm-compatible package manager. You need Node.js and React in your project. The library is built specifically for React, so it will not work in projects that use other frameworks.

  • Prerequisites: Node.js, pnpm or another npm-compatible package manager, and React
pnpm install cmdk

Build a basic command menu

Import the Command component and nest the parts inside it. Command.Input is the search field, Command.List holds the results, and Command.Item is each selectable entry. Command.Group adds a heading, Command.Separator divides items, and Command.Empty shows a message when nothing matches.

  • Command.Input: the search field, forwards props to the underlying input element
  • Command.List: contains items and groups
  • Command.Item: an entry that becomes active on pointer enter
  • Command.Group: groups items under a heading
  • Command.Empty: renders when there are no results
import { Command } from 'cmdk' const CommandMenu = () => { return ( <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> ) }

Open the menu with Cmd+K or Ctrl+K

To make the menu open from the keyboard, use Command.Dialog. Store the open state in React, then listen for keydown events on the document. When the k key is pressed together with the Meta key (Cmd on macOS) or Ctrl, call preventDefault and toggle the state. Pass the state to Command.Dialog with open and onOpenChange.

Command.Dialog composes Radix UI's Dialog component, so Radix Dialog props are forwarded to it. The overlay is always rendered.

import { Command } from 'cmdk' const CommandMenu = () => { const [open, setOpen] = React.useState(false) // Toggle the menu when ⌘K is pressed React.useEffect(() => { const down = (e) => { if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { e.preventDefault() setOpen((open) => !open) } } document.addEventListener('keydown', down) return () => document.removeEventListener('keydown', down) }, []) return ( <Command.Dialog open={open} onOpenChange={setOpen} label="Global Command Menu"> <Command.Input /> <Command.List> <Command.Empty>No results found.</Command.Empty> <Command.Item>Apple</Command.Item> </Command.List> </Command.Dialog> ) }

Control filtering, keywords and value

cmdk filters and sorts items automatically. You can supply a custom filter function, which receives the item value and the search text and returns a rank. A third argument, keywords, lets you add aliases to an item. Values and keywords are trimmed. If you want to handle filtering yourself, set shouldFilter to false.

You can make arrow keys wrap around the list with the loop prop. The value and onValueChange props let you control the selected item from your own state.

  • Custom filter: receives (value, search, keywords) and returns a number
  • Keywords: add aliases to an item with the keywords prop on Command.Item
  • shouldFilter={false}: disables built-in filtering and sorting
  • loop: arrow keys wrap from the last item back to the first
<Command filter={(value, search, keywords) => { const extendValue = value + ' ' + keywords.join(' ') if (extendValue.includes(search)) return 1 return 0 }} />

Style the menu yourself

cmdk ships without default styling, so you write the CSS. Every part forwards props, including ref, and has a data-attribute starting with cmdk- that you can target. For example, the list is [cmdk-list], and items carry [cmdk-item] along with [data-selected] and [data-disabled] states.

To animate the list height, use the --cmdk-list-height CSS variable. You can also use scroll-padding to bring items into view earlier near the edges of the viewport.

  • Root: [cmdk-root]
  • Dialog and overlay: [cmdk-dialog] and [cmdk-overlay]
  • Input: [cmdk-input]
  • Items: [cmdk-item] with [data-selected] and [data-disabled]
  • Groups: [cmdk-group], hidden with the hidden attribute when filtered out
[cmdk-list] { min-height: 300px; height: var(--cmdk-list-height); max-height: 500px; transition: height 100ms ease; }

Use nested pages and async loading

For a menu where selecting an item opens a deeper set of items, cmdk's README describes treating each set as a page, stored in simple React state. Escape or Backspace on an empty search can step back to the previous page.

For asynchronous results, render Command.Loading conditionally while data loads. The useCommandState hook lets you read a slice of menu state, for example the current search text, to show a more detailed empty message. The README notes this hook is for advanced use cases.

What people use it for

  • Adding a global Cmd+K search-and-jump menu to a web app
  • Letting users run actions such as changing a theme from a keyboard-driven popup
  • Building a searchable combobox with an accessible keyboard interface
  • Rendering a menu from custom components or static JSX items
  • Creating a multi-level menu where selecting an item opens a deeper page of choices

Frequently asked questions

Is cmdk free to use?

cmdk is released under the MIT licence. The source material for this guide does not give further licence terms, so check the repository's licence file for details.

What do I need to install cmdk?

The source lists Node.js, pnpm or another npm-compatible package manager, and React as prerequisites. Install it with pnpm install cmdk.

Does cmdk come with styles?

No. cmdk is intentionally unstyled, so you write your own CSS. You target its cmdk- data-attributes such as [cmdk-list] and [cmdk-item].

Can I use cmdk without React?

No. The library is built specifically for React, so projects that do not use React are not a good fit.

How is cmdk different from kbar or Downshift?

The source lists kbar, react-cmdk and Downshift as comparable projects but does not compare them in detail. The source says cmdk is useful if you want full control over the look and composable items, and that it builds on Radix UI for its dialog.

Why does Cmd+K not open my menu?

Check that your keydown listener is attached to the document, that it checks e.metaKey or e.ctrlKey, and that it calls preventDefault and toggles the open state. The source's example follows this pattern.

New agent skills, repos and AI tools — every week

The best new skills and open-source projects, the fastest-growing repos, and one free prompt. Five minutes to read. Unsubscribe anytime.

Our verdict

cmdk suits React developers who want a command palette they can design fully themselves, especially if they already use Radix UI. If you need a visually finished menu out of the box, or you are not writing React code, pick a pre-styled widget or a ready-made app instead.

Install options & downloads