@mshafiqyajid/react-command-palette
βK command palette for React. Headless hook + styled component. Fuzzy match against label/hint/keywords, item grouping, recent-items section, full keyboard navigation, configurable global hotkey (βK / Ctrl+K). Zero dependencies, fully typed.
Playground #
Props
TSX
import { CommandPaletteStyled } from "@mshafiqyajid/react-command-palette/styled";
import "@mshafiqyajid/react-command-palette/styles.css";
<CommandPaletteStyled
items={items}
open={open}
onOpenChange={setOpen}
onSelect={(item) => /* β¦ */}
withRecent
withFooter
/>Install #
npm install @mshafiqyajid/react-command-palette Quick start #
import { CommandPaletteStyled } from "@mshafiqyajid/react-command-palette/styled";
import "@mshafiqyajid/react-command-palette/styles.css";
const items = [
{ id: "new", label: "New file", group: "File", shortcut: "βN" },
{ id: "open", label: "Open recent", group: "File", keywords: ["history"] },
{ id: "find", label: "Find in files", group: "Edit", shortcut: "βF" },
{ id: "settings", label: "Open settings", group: "App" },
];
<CommandPaletteStyled
items={items}
onSelect={(item) => console.log(item.id)}
recentStorageKey="my-app-cmd-recent"
/> Opens automatically on βK / Ctrl+K. Up/Down moves, Enter selects, Esc closes.
Headless #
import { useCommandPalette } from "@mshafiqyajid/react-command-palette";
const cmd = useCommandPalette({ items, onSelect });
return cmd.isOpen ? (
<div role="dialog">
<input {...cmd.inputProps} placeholder="Type a commandβ¦" />
<ul {...cmd.listProps}>
{cmd.groups.map((g) => (
<section key={g.id}>
<h4>{g.label}</h4>
{g.items.map((item) => (
<li key={item.id} {...cmd.getItemProps(item)}>{item.label}</li>
))}
</section>
))}
</ul>
</div>
) : null; API #
| Prop | Type | Default | Description |
|---|---|---|---|
| items | CommandItem[] | β | Required. Items to render. |
| onSelect | (item) => void | β | Fires on Enter / click |
| defaultOpen / open / onOpenChange | boolean | false | Open state (uncontrolled or controlled) |
| defaultQuery | string | "" | Initial search value |
| filter | (item, q) => boolean | substring match | Custom filter (fuzzy/score) |
| recentStorageKey | string | β | Persists last 5 selections via localStorage |
| hotkey | { key, meta?, ctrl?, shift? } | null | { key: "k", meta: true, ctrl: true } | Global open shortcut |
| placeholder | string | "Type a commandβ¦" | Search input placeholder |
| emptyText | ReactNode | "No results." | Shown when no items match |
| emptyState | (query) => ReactNode | β | Custom empty state β receives the active query. Overrides emptyText. |
| loading | boolean | false | Show a loading row instead of items (use with async filtering) |
| loadingText | ReactNode | "Loadingβ¦" | Loading row text |
| highlightMatches | boolean | true | Bolden matched query characters in labels |
| footer | ReactNode | β | Footer slot (keyboard hints, etc) |
CommandItem #
| Field | Type | Description |
|---|---|---|
| id | string | Required, unique |
| label | string | Required, matched by filter |
| hint | string? | Secondary text + matched by filter |
| icon | ReactNode? | Left-side icon |
| shortcut | string? | Right-side keyboard hint |
| group | string? | Section heading |
| disabled | boolean? | Skipped by keyboard nav |
| keywords | string[]? | Extra search terms |
| data | T? | Free-form payload |