Skip to content

CommandPalette

CommandPalette is a search field over a list of commands or pages, in a Modal: the ⌘K menu, a quick switcher or a site search. It follows cmdk, Raycast, Linear and Algolia DocSearch.

It doesn’t know your data. You pass items (or groups of them) and it either filters them with a simple built-in match or shows them as given, when you search yourself on a server or with a full-text index. On wide screens it is a dialog pinned near the top of the viewport, so it doesn’t jump as results come and go; under 640px it is a full-height sheet with the field at the top, clear of the on-screen keyboard.

Demos

Basic

show and onUpdateShow control it, and onSelect receives the picked item. The built-in filter matches the search text anywhere in a label or in its keywords, ignoring case, and hides groups left empty. The match is highlighted in the label.

Icons, descriptions and shortcuts

An item can have an icon, a description line and a suffix at the end of the row, such as its own shortcut. A disabled item is shown but skipped by the arrow keys.

Open with a shortcut

useCommandPaletteShortcut(onTrigger, "mod+k") listens on the page for ⌘K on Apple platforms and Ctrl+K elsewhere, and stops the browser’s own use of it. commandPaletteShortcutKeys gives the keycaps for Kbd. Press the shortcut while this page has focus.

An item with an href is a real link: a click follows it, a middle click or ⌘ / Ctrl-click opens a new tab. From the keyboard, Enter follows it and ⌘ / Ctrl / Shift + Enter opens it in a new tab, leaving the palette open. target works as on any link.

For remote or full-text search, set filter={false}, search on onUpdateQuery, and pass the results as items; loading shows a spinner and keeps the previous results until new ones arrive. Here onSelect calls event.preventDefault() to route a link itself, as a client-side router would.

Custom rows

renderItem draws the content of each row. It receives whether the row is active and the label with the match already highlighted.

Accessibility

It follows the WAI-ARIA combobox pattern with a listbox popup. The field is a combobox that keeps focus the whole time; ↑ / ↓ move the active option (wrapping, skipping disabled ones), Home / End jump to the first and last, Enter picks it, and Escape closes the palette. The active option is announced through aria-activedescendant, and a polite live region reports the number of results. Keys that confirm an IME candidate never pick an option. Groups are groups named by their labels.

Props

NameTypeDefaultDescriptionVersion
itemsCommandPaletteMixedItem[]requiredItems and groups ({ type: 'group', key, label, children }).Unreleased
showbooleanundefinedControlled visibility.Unreleased
defaultShowbooleanfalseInitial visibility when uncontrolled.Unreleased
onUpdateShow(show: boolean) => voidundefinedCalled with the new visibility.Unreleased
querystringundefinedControlled search text.Unreleased
defaultQuerystring''Initial search text when uncontrolled.Unreleased
onUpdateQuery(query: string) => voidundefinedCalled on every edit of the search text.Unreleased
filterboolean | (item, query) => booleantruetrue matches a case-insensitive substring of the label or a keyword; a function replaces the test; false shows items as given.Unreleased
highlightbooleantrueMark the search text in labels.Unreleased
onSelect(item, event) => voidundefinedCalled on a pick by click or Enter. For a link, event.preventDefault() routes it yourself.Unreleased
closeOnSelectbooleantrueClose after a pick. A link opened in a new tab keeps it open.Unreleased
loadingbooleanfalseA spinner in the field, and “Searching…” while there are no results.Unreleased
emptyReactNodeundefinedShown when a search finds nothing. Defaults to Empty with “No results”.Unreleased
renderItem(item, { active, query, label }) => ReactNodeundefinedReplaces the content of each row.Unreleased
placeholderstringundefinedPlaceholder and accessible name of the field. Defaults to the locale’s.Unreleased
labelstringundefinedAccessible name of the dialog. Defaults to “Command palette”.Unreleased
footerReactNodeundefinedReplaces the keyboard hints.Unreleased
showFooterbooleantrueShow the footer. The built-in hints are hidden on touch screens.Unreleased
maskClosablebooleantrueClose on a click on the mask.Unreleased
tostring | HTMLElement | falseundefinedWhere to mount; see Modal.Unreleased
zIndexnumberundefinedA fixed z-index instead of stacking above earlier layers.Unreleased
onAfterEnter() => voidundefinedCalled when the enter animation has finished.Unreleased
onAfterLeave() => voidundefinedCalled when the leave animation has finished.Unreleased
classNamestringundefinedClass of the palette’s surface.Unreleased
styleCSSPropertiesundefinedStyle of the palette’s surface.Unreleased
inputRefRef<HTMLInputElement>undefinedThe search field.Unreleased

CommandPaletteItem

NameTypeDescription
keystring | numberUnique among all items.
labelstringThe text shown, matched and highlighted.
descriptionReactNodeA second, quieter line.
iconReactNodeShown before the label.
suffixReactNodeShown at the end of the row.
keywordsstring[]More words the built-in filter matches.
hrefstringMakes the row a link.
targetstringThe link’s target.
disabledbooleanShown but can’t be picked.

useCommandPaletteShortcut

useCommandPaletteShortcut(onTrigger, shortcut = "mod+k", { enabled = true }) calls onTrigger on the shortcut anywhere on the page. A shortcut is modifiers (mod, ctrl, meta, alt, shift) and one key joined by +; mod is ⌘ on Apple platforms and Ctrl elsewhere, and a shifted symbol is written as itself (?). Pass an array for several. A shortcut without Ctrl, ⌘ or Alt, such as /, is ignored while typing in a field. commandPaletteShortcutKeys(shortcut) returns the keycaps to show on this platform, such as ["⌘", "K"].