⌘K is a command menu React component that can also be used as an accessible combobox

Overview

⌘K cmdk minzip package size cmdk package version

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

Demo and examples: cmdk.paco.me

Install

$ npm install cmdk

Use

No results found. a b c Apple ) }">
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>
  )
}

Or in a dialog:

No results found. a b c Apple ) }">
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) {
        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.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>
  )
}

Parts and styling

All parts forward props, including ref, 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 a elevated context. Can be controlled with the value and onValueChange props.

Note

Values are always converted to lowercase and trimmed. Use apple, not Apple.

const [value, setValue] = React.useState('apple')

return (
  <Command value={value} onValueChange={setValue}>
    <Command.Input />
    <Command.List>
      <Command.Item>Orange</Command.Item>
      <Command.Item>Apple</Command.Item>
    </Command.List>
  </Command>
)

You can provide a custom filter function that is called to rank each item. Both strings are normalized as lowercase and trimmed.

<Command
  filter={(value, search) => {
    if (value.includes(search)) return 1
    return 0
  }}
/>

Or disable filtering and sorting entirely:

<Command shouldFilter={false}>
  <Command.List>
    {filteredItems.map((item) => {
      return (
        <Command.Item key={item} value={item}>
          {item}
        </Command.Item>
      )
    })}
  </Command.List>
</Command>

Dialog [cmdk-dialog] [cmdk-overlay]

Props are forwarded to Command. Composes Radix UI's Dialog component. The overlay is always rendered. See the Radix Documentation for more information. Can be controlled with the open and onOpenChange props.

const [open, setOpen] = React.useState(false)

return (
  <Command.Dialog open={open} onOpenChange={setOpen}>
    ...
  </Command.Dialog>
)

Input [cmdk-input]

All props are forwarded to the underlying input element. Can be controlled with the value and onValueChange props.

const [search, setSearch] = React.useState('')

return <Command.Input value={search} onValueChange={setSearch} />

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] [aria-disabled?] [aria-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.

Apple ">
<Command.Item
  onSelect={(value) => console.log('Selected', value)}
  // Value is implicity "apple" because of the provided text content
>
  Apple
</Command.Item>

Group [cmdk-group] [hidden?]

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

Apple ">
<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. This may be relevant in your styling.

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.

const [loading, setLoading] = React.useState(false)

return <Command.List>{loading && <Command.Loading>Hang on…</Command.Loading>}</Command.List>

useCmdk(state => state.selectedField)

Hook that composes useSyncExternalStore. Pass a function that returns a slice of the command menu state to re-render when that slice changes. This hook is provided for advanced use cases and should not be commonly used.

A good use case would be to render a more detailed empty state, like so:

">
const search = useCmdk((state) => state.search)
return <Command.Empty>No results found for "{search}".</Command.Empty>

Examples

Code snippets for common use cases.

Nested items

Often selecting one item should navigate deeper, with a more refined set of items. For example selecting "Change theme…" should show new items "Dark theme" and "Light theme". We call these sets of items "pages", and they can be implemented with simple state:

const ref = React.useRef(null)
const [open, setOpen] = React.useState(false)
const [search, setSearch] = React.useState('')
const [pages, setPages] = React.useState([])
const page = pages[pages.length - 1]

return (
  <Command
    onKeyDown={(e) => {
      // Escape goes to previous page
      // Backspace goes to previous page when search is empty
      if (e.key === 'Escape' || (e.key === 'Backspace' && !search)) {
        e.preventDefault()
        setPages((pages) => pages.slice(0, -1))
      }
    }}
  >
    <Command.Input value={search} onValueChange={setSearch} />
    <Command.List>
      {!page && (
        <>
          <Command.Item onSelect={() => setPages([...pages, 'projects'])}>Search projects…</Command.Item>
          <Command.Item onSelect={() => setPages([...pages, 'teams'])}>Join a team…</Command.Item>
        </>
      )}

      {page === 'projects' && (
        <>
          <Command.Item>Project A</Command.Item>
          <Command.Item>Project B</Command.Item>
        </>
      )}

      {page === 'teams' && (
        <>
          <Command.Item>Team 1</Command.Item>
          <Command.Item>Team 2</Command.Item>
        </>
      )}
    </Command.List>
  </Command>
)

Show sub-items when searching

If your items have nested sub-items that you only want to reveal when searching, render based on the search state:

const SubItem = (props) => {
  const search = useCmdk((state) => state.search)
  if (!search) return null
  return <Command.Item {...props} />
}

return (
  <Command>
    <Command.Input />
    <Command.List>
      <Command.Item>Change theme…</Command.Item>
      <SubItem>Change theme to dark</SubItem>
      <SubItem>Change theme to light</SubItem>
    </Command.List>
  </Command>
)

Asynchronous results

Render the items as they become available. Filtering and sorting will happen automatically.

const [loading, setLoading] = React.useState(false)
const [items, setItems] = React.useState([])

React.useEffect(() => {
  async function getItems() {
    setLoading(true)
    const res = await api.get('/dictionary')
    setItems(res)
    setLoading(false)
  }

  getItems()
}, [])

return (
  <Command>
    <Command.Input />
    <Command.List>
      {loading && <Command.Loading>Fetching words…</Command.Loading>}
      {items.map((item) => {
        return (
          <Command.Item key={`word-${item}`} value={item}>
            {item}
          </Command.Item>
        )
      })}
    </Command.List>
  </Command>
)

Use inside Popover

We recommend using the Radix UI popover component. ⌘K relies on the Radix UI Dialog component, so this will reduce your bundle size a bit due to shared dependencies.

$ npm install @radix-ui/react-popover

Render Command inside of the popover content:

import * as Popover from '@radix-ui/react-popover'

return (
  <Popover.Root>
    <Popover.Trigger>Toggle popover</Popover.Trigger>

    <Popover.Content>
      <Command>
        <Command.Input />
        <Command.List>
          <Command.Item>Apple</Command.Item>
        </Command.List>
      </Command>
    </Popover.Content>
  </Popover.Root>
)

Drop in stylesheets

You can find global stylesheets to drop in as a starting point for styling. See website/styles/cmdk for examples.

FAQ

Accessible? Yes. Labeling, aria attributes, and DOM ordering tested with Voice Over and Chrome DevTools. Dialog composes an accessible Dialog implementation.

Virtualization? No. Good performance up to 2,000-3,000 items, though. Read below to bring your own.

Filter/sort items manually? Yes. Pass shouldFilter={false} to Command. Better memory usage and performance. Bring your own virtualization this way.

React 18 safe? Yes, required. Uses React 18 hooks like useId and useSyncExternalStore.

Unstyled? Yes, use the listed CSS selectors.

Hydration mismatch? No, likely a bug in your code. Ensure the open prop to Command.Dialog is false on the server.

React strict mode safe? Yes. Open an issue if you notice an issue.

Weird/wrong behavior? Make sure your Command.Item has a key and unique value.

Concurrent mode safe? Maybe, but concurrent mode is not yet real. Uses risky approaches like manual DOM ordering.

React server component? No, it's a client component.

Listen for ⌘K automatically? No, do it yourself to have full control over keybind context.

React Native? No, and no plans to support it. If you build a React Native version, let us know and we'll link your repository here.

History

Written in 2019 by Paco (@pacocoursey) to see if a composable combobox API was possible. Used for the Vercel command menu and autocomplete by Rauno (@raunofreiberg) in 2020. Re-written independently in 2022 with a simpler and more performant approach. Ideas and help from Shu (@shuding_).

use-descendants was extracted from the 2019 version.

Comments
  • Better example code / tutorial?

    Better example code / tutorial?

    I'm unable to get a simple example running with any of the example code on the website in a React Codesandbox. Is there any fully working example that can be provided?

    import "./styles.css";
    import React from "react";
    import { Command } from "cmdk";
    
    export default function App() {
      const loading = true;
      const setOpen = () => {};
      const open = true;
    
      return (
        <div className="App">
          <h1>Hello CodeSandbox</h1>
          <h2>Start editing to see some magic happen!</h2>
    
          <Command.Dialog open={open} onOpenChange={setOpen}>
            <Command.Input />
    
            <Command.List>
              {loading && <Command.Loading>Hang on…</Command.Loading>}
    
              <Command.Empty>No results found.</Command.Empty>
    
              <Command.Group heading="Fruits">
                <Command.Item>Apple</Command.Item>
                <Command.Item>Orange</Command.Item>
                <Command.Separator />
                <Command.Item>Pear</Command.Item>
                <Command.Item>Blueberry</Command.Item>
              </Command.Group>
    
              <Command.Item>Fish</Command.Item>
            </Command.List>
          </Command.Dialog>
        </div>
      );
    }
    

    https://codesandbox.io/s/wizardly-currying-x68myq

    opened by ismailuddin 5
  • Error when using useCommandState

    Error when using useCommandState

    I have just added useCommandState to my cmdk component and before anything is even typed this error is shown. I am using the latest version of next.js (v12.3.1), React (v18.2.0) and cmdk (v0.1.20). Do you have any idea why this might be occurring? Is there anything else that I need to add in order for this to work?

    Usage

    const search = useCommandState((state) => state.search)
    

    Error

    TypeError: Cannot read properties of undefined (reading 'subscribe')
    
    opened by mrmartineau 2
  • prune: yarn.lock; docs(website): pnpm; chore: enforce pnpm

    prune: yarn.lock; docs(website): pnpm; chore: enforce pnpm

    Addition to #18

    • Enforce pnpm at the root install for workspaces
    • Removes old yarn.lock files from the move to pnpm
    • Contributors for localized ./website development for cmdk should use pnpm
      • Separate from the overall README which people can install cmdk via their preferred package manager
    opened by JeromeFitz 2
  • Use [hidden] on group instead of changing render structure

    Use [hidden] on group instead of changing render structure

    Fixes #26

    I risked this change in c95c9a0 but didn't realize each item would be remounted with a new id when the group is hidden, which breaks a lot of logic that relies on each item having a stable id.

    opened by pacocoursey 2
  • Run `playwright test` on every PR push

    Run `playwright test` on every PR push

    We should set up a GitHub action to test changes made in every PR via the existing Playwright setup. Would complement nicely with the Vercel deployment.

    help wanted good first issue 
    opened by pacocoursey 2
  • remove dollar sign from code snippet

    remove dollar sign from code snippet

    Might want to remove the dollar sign from the $ npm install cmdk code snippet since using the copy button next to it copies the whole thing into clipboard which causes errors when pasting into terminals

    opened by kuldar 1
  • Not working on NextJS

    Not working on NextJS

    Hi @raunofreiberg, for some reason, I’m unable to get it to work on NextJS (using latest next and react, react-dom). I’ve included a basic reproduction here: cmdk-nextjs.

    As you can see below, nothing happens. I must be missing something basic, but have no idea what I’m missing.

    CleanShot 2022-08-17 at 08 47 58

    opened by heymartinadams 1
  • Fix group filtering

    Fix group filtering

    Closes https://github.com/pacocoursey/cmdk/issues/30 & https://github.com/pacocoursey/cmdk/issues/24 (https://github.com/pacocoursey/cmdk/pull/25)

    opened by flaviouk 1
  • NextJS Support

    NextJS Support

    Does this library officially support NextJS, or have any plans to? When trying to integrate it to a larger existing NextJS codebase, I received the following error:

    Unhandled Runtime Error
    TypeError: react__WEBPACK_IMPORTED_MODULE_0__.useId is not a function
    
    Call Stack
    eval
    node_modules/cmdk/dist/index.mjs (1:719)
    

    However, when trying to make a minimal reproduction, I failed to get it to load at all, with Next complaining of a missing webpack loader -- not sure what loader is required in this instance.

    See codesandbox here: https://codesandbox.io/s/charming-darkness-gw9ezm

    TL;DR - Library doesn't seem to work with NextJS right now.

    opened by joshcawthorne 1
  • Sometimes cannot go down with down arrow

    Sometimes cannot go down with down arrow

    Hey all, thanks for making this. I was playing around with the demo on the website and found two bugs that I'll lump in one here. First if I go to the Framer example and type "baa", then delete the last "a", the only result showing is not highlighted which seems like a bug to me. However I can highlight it by going down with the down arrow.

    Then if I delete another "a", I see two results, but I cannot go up/down with the arrows anymore.

    Here's a video where I'm pressing up/down arrow at the end.

    https://user-images.githubusercontent.com/4534692/183738721-7e2a009e-c38e-484d-808c-e1f3e369868d.mov

    bug 
    opened by bsansouci 1
  • Convert to pnpm workspace

    Convert to pnpm workspace

    Changes from hardcoded cmdk versions to using the workspace:* version.

    This also makes it so people other than me can build the source code because previously this was in my personal monorepo and relied on deps outside of this repo.

    opened by pacocoursey 1
  • Ignore Safari pointer events on keyboard navigation

    Ignore Safari pointer events on keyboard navigation

    Safari will fire pointer events when navigating via the keyboard, annoyingly causing the active index to jump back up to where the cursor is resting. Instead, these should be ignored when caused via keyboard navigation.

    Could be solved by #49

    bug enhancement 
    opened by pacocoursey 0
  • Option to not change selected item via mouse events

    Option to not change selected item via mouse events

    I'm building a raycast clone for linux as a learning project, using cmdk and wails.io. It worked really well for me so far, thanks a lot for releasing this !

    One pain point I have is the keyboard/mouse interaction during the selection. When I scroll a list using a keyboard, the mouse is often stealing the focus.

    https://user-images.githubusercontent.com/17577332/188937337-b5bd1537-d63d-47b0-9e75-8008ea5fb222.mov

    I love how raycast solve the issue: the mouse only provide an hover effect, and only select on click.

    https://user-images.githubusercontent.com/17577332/188936938-39d7154b-770a-4338-be39-5ee5dbe2d4d9.mov

    Would you be interested in adding this behaviour to the app ? I can work on the pr.

    enhancement 
    opened by pomdtr 3
  • Expose getters / setters for selected index?

    Expose getters / setters for selected index?

    I wanted to be able to control the selected index from outside the component if possible. Motivation is dealing with sub pages and parent pages. If I go into a sub page and then back out hitting Escape, I wanted the parent page Item to have focus in the List.

    Looks like internally there are a couple of helper methods like updateSelectedByChange and updateSelectedToIndex that I wanted to get access to or be able to pass a prop to the Command component indicating initial Item to be selected/focused.

    Thanks for putting this library out, seems to be working well for me so far!

    enhancement 
    opened by agamrp 2
  • Fix textContent not working as expected

    Fix textContent not working as expected

    Fixes #40

    When using textContent instead of value prop, and the element is removed from the dom, useValue sets and returns undefined until the input is empty again. I added the test to prove the problem.

    opened by caumeira 1
  • Consider automating publishing to npm

    Consider automating publishing to npm

    It takes very little effort with tools like https://intuit.github.io/auto/index

    The idea would be to automatically publish an alpha version to npm on every pr commit and based on pr labels publish it as a patch/minor/major version once it's in main

    An example I've done here: https://github.com/flaviouk/standard-monorepo/blob/main/.github/workflows/release.yml#L31-L35

    enhancement 
    opened by flaviouk 0
Releases(v0.1.19)
  • v0.1.19(Aug 17, 2022)

    What's Changed

    • Fix group filtering when shouldFilter is disabled by @flaviouk in https://github.com/pacocoursey/cmdk/pull/33
    • Add prettier by @flaviouk in https://github.com/pacocoursey/cmdk/pull/36

    New Contributors

    • @flaviouk made their first contribution in https://github.com/pacocoursey/cmdk/pull/33

    Full Changelog: https://github.com/pacocoursey/cmdk/compare/v0.1.18...v0.1.19

    Source code(tar.gz)
    Source code(zip)
  • v0.1.18(Aug 9, 2022)

    What's Changed

    • Use [hidden] on group instead of changing render structure by @pacocoursey in https://github.com/pacocoursey/cmdk/pull/27

    Full Changelog: https://github.com/pacocoursey/cmdk/commits/v0.1.18

    Source code(tar.gz)
    Source code(zip)
  • v0.1.17(Aug 9, 2022)

Owner
Paco
Crafting interfaces
Paco
React-verification-input is a customizable, masked input that can be used to enter all sorts of codes

react-verification-input is a customizable, masked input that can be used to enter all sorts of codes e.g. security codes when two-factor authenticating. Also I'm sure you can think of many more creative use cases.

Andreas Willi 72 Sep 20, 2022
Material-UI is a simple and customizable component library to build faster, beautiful, and more accessible React applications. Follow your own design system, or start with Material Design.

Material-UI Quickly build beautiful React apps. Material-UI is a simple and customizable component library to build faster, beautiful, and more access

Material-UI 81.4k Sep 22, 2022
Rheostat is a www, mobile, and accessible slider component built with React

Rheostat A mobile, tablet, desktop, and accessible slider for the web. Install npm install rheostat Initialize As of v3.0.0, the rheostat project reli

Airbnb 1.7k Sep 22, 2022
InvaUI is a simple, modular and accessible component library that gives you the building blocks you need to build your React applications.

Inva UI Sponsored by English • Table of Contents Quick start Install Install The easiest way to use Inva UI is to install it from npm $ npm install @i

Shaun Mak 4 Nov 8, 2021
A React component that renders a Likert scale. It is responsive and accessible.

React Likert Scale A React component that makes a Likert Scale for collecting data or to make a survey. It has the following features: it is fully res

Craig Creeger 3 Sep 24, 2021
An open-source UI component library for building high-quality, accessible design systems and web apps

An open-source UI component library for building high-quality, accessible design systems and web apps

Radix 6.1k Oct 1, 2022
⚡️ Simple, Modular & Accessible UI Components for your React Applications

Build Accessible React Apps with Speed ⚡️ Chakra UI provides a set of accessible, reusable, and composable React components that make it super easy to

Chakra UI 28.8k Sep 30, 2022
Toolkit for building accessible rich web apps with React

Reakit Toolkit for building accessible rich web apps with React. Explore website » Sponsors By donating $100 or more you become a sponsor and help in

Reakit 6.2k Sep 29, 2022
An accessible accordion built using React.

An accessible accordion built using React.

Peter Aiello 14 Sep 16, 2022
A reusable React implementation of accessible footnotes.

react-a11y-footnotes react-a11y-dialog is a thin React component to ease the use of accessible footnotes in React applications. This implementation is

Kitty Giraudel 22 Aug 20, 2022
Styx UI provides a set of accessible, reusable, and minimalist React components

Set of Accessible, Reusable & Minimalist React Components for Web.

Harsh Karande 1 Sep 18, 2022
Completely unstyled, headless and accessible React UI components.

React Styleless Ui Completely unstyled, headless and accessible React UI components. Public Roadmap Our project roadmap is where you can learn about w

StylelessUi 15 Sep 9, 2022
Collection of headless components/hooks that are accessible, composable, customizable from low level to build your own UI & Design System

Collection of headless components/hooks that are accessible, composable, customizable from low level to build your own UI & Design System powered by Reakit System.

Timeless 196 Aug 11, 2022
A set of completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS.

Headless UI A set of completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS. Documentation For full

Tailwind Labs 16.9k Sep 30, 2022
Pancake UIkit is a set of React components and hooks used to build pages on Pancake's apps.

?? Pancake UIkit Pancake UIkit is a set of React components and hooks used to build pages on Pancake's apps. It also contains a theme file for dark an

PolyPup 1 Nov 4, 2021
A set of React components and hooks used to build pages on Wakanda's apps

Wakanda UIkit is a set of React components and hooks used to build pages on Wakanda's apps. It also contains a theme file for dark and light mode.

wakandaswap 0 Dec 16, 2021
YGDL Network UIkit is a set of React components and hooks used to build pages on Zaigar Finance's apps

YGDL Network UIkit is a set of React components and hooks used to build pages on Zaigar Finance's apps. It also contains a theme file for dark and light mode.

null 0 Feb 6, 2022
This package contains the Renderer and core utilities used by tldraw.

@tldraw/core This package contains the Renderer and core utilities used by tldraw. You can use this package to build projects like tldraw, where React

tldraw 418 Sep 1, 2022
Sudoku solver with a UI showing step-by-step techniques used

Sudoku Solver This project contains both a web UI to play Sudoku, and an educational Sudoku solver. The web UI lets you play a sudoku grid, as well as

null 0 Feb 8, 2022