Skip to content
Cynosurev0.5.0
SearchStorybookGitHubnpmColour mode: System. Switch to Light

Components / Overlays

CommandPalette

A search box over every command in the product. Press Ctrl+K (Command+K on a Mac), type a few letters, press Enter. It is the fastest way to reach an action without hunting through menus.

Keywords groups and wrapping arrows

When to useLink to "When to use"

  • Products with many actions spread across menus and panels: inspectors, editors, database tools.

Use something else when:

  • the actions belong to one button: a Menu
  • people pick a value for a field: a Combobox

UsageLink to "Usage"

tsx
Copy
import { CommandPalette } from "@arshad-shah/cynosure";

<CommandPalette
  commands={[
    { id: "save", label: "Save file", group: "File", shortcut: ["Ctrl", "S"], keywords: ["write", "disk"] },
    { id: "preview", label: "Toggle preview", group: "View" },
  ]}
  onRun={(id) => actions[id]()}
/>

Mount it once near the root. It listens for its shortcut (mod+k by default) anywhere on the page, shadow roots included. A command's shortcut is only shown in the row; bind those keys with useHotkey.

Finding commandsLink to "Finding commands"

Every typed word must appear in the label or the keywords, in any order, and commands whose label starts with the first word come first. Each typed word is marked where it appears in a label (HighlightedText); the option is still named by its whole label. Before anything is typed, the commands run most recently come first under "Recent".

Recent commands come first

Before anything is typed, commands are listed under their group headings. Search results are one flat list in rank order, so each row shows its group as a small Badge between the label and the shortcut; recent commands, which mix groups, carry the tag too.

Search results are flat with category tags

filter(command, query) replaces the built-in search, for an app with its own fuzzy matcher. It is called for each command while there is a query (trimmed), and answers with a match, true, or null:

tsx
Copy
<CommandPalette commands={commands} onRun={run} filter={(command, query) => fuzzyMatch(query, command.label)} />
  • A match { score, positions } keeps the command. Lower scores come first, as fuzzy matchers score them; ties, and matches without a score, keep the order of commands. positions are the indices of the matched characters in the label, and exactly those are marked.
  • true keeps the command, unmarked. null or false leaves it out.
Custom filter ranks and marks its positions

Shortcuts with useHotkeyLink to "Shortcuts with useHotkey"

tsx
Copy
import { useHotkey } from "@arshad-shah/cynosure";

useHotkey("mod+s", save);
useHotkey("/", focusSearch); // plain keys are ignored while typing in a field

Shortcuts with Ctrl, Command or Alt work everywhere, text fields included. Plain keys are ignored in fields, so "/" still types a slash. Add data-cy-hotkeys="off" to a subtree (a code editor with its own bindings) to opt it out.

PropsLink to "Props"

Every prop of CommandPalette
PropWhat it doesTypeDefault
commandsRequired
The commands to search and run.Command[]
onRunRequired
Called with the chosen command's id; the palette then closes.(id: string) => void
open
Controlled: whether the palette is open.boolean
defaultOpen
Open at first, when uncontrolled.boolean
onOpenChange
Called when the palette opens or closes (shortcut, Escape, a press outside, running a command), with the new state.((open: boolean) => void)
shortcut
Global shortcut that toggles the palette (mod is Command on Apple platforms, else Control). false: none.string | false
filter
Your own search, called for each command while there is a query: return a CommandMatch ({ score, positions }, as fuzzy matchers return it), true to keep the command unmarked, or null/false to leave it out. Default: every word of the query in the label or keywords, label prefixes first, the words marked.((command: Command, query: string) => CommandMatch | boolean | null | undefined)
recentLimit
How many recently run commands to list before you type (default 5, 0 for none).number
labels
Replace the built-in English text: the palette's name, the placeholder, the empty note and the recent heading.Partial<CommandPaletteLabels>
className
Added to the panel, for layout.string
style
Layout only, applied to the panel.CSSProperties

Generated from the type definitions in @arshad-shah/cynosure 0.5.0.

Behaviour and accessibilityLink to "Behaviour and accessibility"

Behaviour and accessibility, table 1
InputResult
Control / Command + KOpens or closes it from anywhere
TypingFilters the commands into one flat list, each tagged with its group
Down arrow / Up arrowNext / previous command, wrapping around; disabled ones are skipped
Enter or a clickRuns the command and closes
Escape or the backdropCloses; focus returns to where it was
  • Roles: a modal dialog named by labels.title, holding a combobox search field and a listbox of options in named groups (search results are one unnamed group). A row's group tag is part of its name. The active command is aria-activedescendant, so focus stays in the search field.
  • Fresh each time: the search field is empty whenever it opens.
  • Shadow DOM: works inside shadow roots (extensions), including the shortcut.
Shortcut opens search runs and returns focus

TouchLink to "Touch"

Command rows are at least 44px tall on coarse pointers. On narrow screens the panel spans the width.

ThemingLink to "Theming"

Tokens: --cy-command-palette-width, --cy-command-palette-offset (distance from the top) and --cy-command-palette-list-max-height. The backdrop uses --cy-dialog-scrim.

Menu · Combobox · Kbd · Dialog

These examples run Cynosure 0.5.0 (commit 50ce30b), the version on npm.

Getting started
Theming
Icons
Accessibility
Code editors and highlighters
Using Cynosure without React
Build-tool adapters
Components
Accordion
AppShell
AspectRatio
Breadcrumbs
Calendar
Card
Container
Disclosure
Divider
DocumentViewport
Grid
Inline
PageHeader
PanZoomCanvas
ScrollArea
SideNav
SortableList
SplitPane
Stack
StatusBar
Tabs
TitleBar
Toolbar
Tree
VirtualList
ZoomControls
Announcer
Avatar
Badge
CodeBlock
DescriptionList
EmptyState
ErrorBoundary
Field
Heading
IconTile
Image
Kbd
List
LoadingDots
Notice
ProgressBar
Prose
Skeleton
Stat
Steps
Table
Text
Button
Checkbox
Checklist
ColorField
ColorSwatches
Combobox
CompareSlider
CopyButton
DateField
FileDrop
IconButton
KeyValueEditor
Link
NumberField
Pagination
PathField
RadioGroup
SearchField
SegmentedControl
Select
SelectionFrame
SignaturePad
Slider
Switch
TagInput
TextArea
TextField
ThemeToggle
TimeField
ButtonBase
ChoiceBase
DialogBase
FileInputBase
HiddenValue
Icon
InputBase
LinkBase
LiveRegion
TextAreaBase
ChatMessage
Choice
DataGrid
DeviceFrame
HighlightedText
HotkeyField
ListRow
OptionList
OverlayFrame
QRCode
RowActions
Section
SiteFooter
SiteHeader
SkipLink
SortButton
VisuallyHidden
CommandPalette
ContextMenu
Dialog
FloatingToolbar
HoverCard
Menu
Menubar
Popover
ProgressOverlay
Sheet
Toast
Tooltip
Changelog