Commander
A spotlight-style command palette with fuzzy search, grouped commands, and keyboard-driven navigation. Press Alt+K from anywhere to invoke it.
Examples
Or press Alt + K
import { CommandPalette, Button } from "@g4rcez/components";
import {
SquaresFourIcon, FolderIcon, GearIcon,
TerminalIcon, FileTextIcon, PlayIcon,
HouseIcon, MagnifyingGlassIcon, SignOutIcon,
} from "@phosphor-icons/react";
import { useState } from "react";
function CommanderExample() {
const [open, setOpen] = useState(false);
const commands = [
{
type: "group",
title: "Navigation",
items: [
{ type: "shortcut", title: "Dashboard", shortcut: "Alt+d", Icon: <SquaresFourIcon size={16} />, action: () => setOpen(false) },
{ type: "shortcut", title: "Projects", shortcut: "Alt+p", Icon: <FolderIcon size={16} />, action: () => setOpen(false) },
{ type: "shortcut", title: "Settings", shortcut: "Alt+s", Icon: <GearIcon size={16} />, action: () => setOpen(false) },
],
},
{
type: "group",
title: "Developer",
items: [
{ type: "shortcut", title: "Console", shortcut: "Alt+t", Icon: <TerminalIcon size={16} />, action: () => setOpen(false) },
{ type: "shortcut", title: "Logs", shortcut: "Alt+g", Icon: <FileTextIcon size={16} />, action: () => setOpen(false) },
{ type: "shortcut", title: "API Playground", shortcut: "Alt+a", Icon: <PlayIcon size={16} />, action: () => setOpen(false) },
],
},
{
type: "group",
title: "Account",
items: [
{ type: "shortcut", title: "Logout", shortcut: "Alt+l", Icon: <SignOutIcon size={16} />, action: () => setOpen(false) },
],
},
];
return (
<>
<Button theme="primary" onClick={() => setOpen(true)}>Open Commander</Button>
<CommandPalette open={open} bind="Alt+k" commands={commands} onChangeVisibility={setOpen} />
</>
);
}Type xyz to see the empty state
import { CommandPalette, Button } from "@g4rcez/components";
import { HouseIcon, MagnifyingGlassIcon } from "@phosphor-icons/react";
import { useState } from "react";
function EmptyStateExample() {
const [open, setOpen] = useState(false);
const commands = [
{
type: "group",
title: "General",
items: [
{ type: "shortcut", title: "Go to Home", shortcut: "Alt+h", Icon: <HouseIcon size={16} />, action: () => setOpen(false) },
{ type: "shortcut", title: "Quick Search", shortcut: "Alt+f", Icon: <MagnifyingGlassIcon size={16} />, action: () => setOpen(false) },
],
},
];
return (
<>
<Button onClick={() => setOpen(true)}>Open Commander</Button>
<CommandPalette
open={open}
commands={commands}
onChangeVisibility={setOpen}
emptyMessage="No commands match your search. Try a different term."
/>
</>
);
}Use ↑ ↓ arrow keys to update the preview, or use the collapsible Projects filter.
import { CommandPalette, Button } from "@g4rcez/components";
import type React from "react";
import type { CommandItemTypes } from "@g4rcez/components";
import { SquaresFourIcon, FolderIcon, GearIcon } from "@phosphor-icons/react";
import { useState } from "react";
const previewRows = [
{ title: "Command palette", status: "In progress" },
{ title: "Project filters", status: "In review" },
{ title: "Keyboard shortcuts", status: "Ready" },
] as const;
const CommandPreview: React.FC<{ command: CommandItemTypes; text: string }> = ({ command, text }) => {
if (command.type !== "shortcut") return null;
const Title = command.title;
const title = typeof Title === "function" ? <Title text={text} /> : Title;
const hint = Array.isArray(command.hint) ? command.hint.join(" · ") : command.hint;
return (
<article className="flex min-w-0 flex-col gap-base">
<div className="flex min-h-24 items-end justify-between gap-base overflow-hidden rounded-card-radius bg-muted p-base">
<div className="min-w-0">
<p className="text-typography-xs font-semibold text-foreground">Workspace</p>
<p className="mt-1 text-typography-sm text-foreground/80">A page in your team space</p>
</div>
<FolderIcon aria-hidden="true" className="shrink-0 text-primary/20" size={64} />
</div>
<header className="flex min-w-0 items-start gap-sm">
<div aria-hidden="true" className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary">
{command.Icon ?? <FolderIcon size={20} />}
</div>
<div className="min-w-0 flex-1">
<p className="text-typography-xs font-medium text-muted-foreground">Workspace page</p>
<h2 className="mt-1 break-words text-typography-xl font-semibold text-foreground">{title}</h2>
</div>
{command.shortcut && (
<kbd className="inline-flex shrink-0 items-center rounded-tag-radius bg-muted px-tag-padding-x py-tag-padding-y font-mono text-typography-xs text-muted-foreground">
{command.shortcut}
</kbd>
)}
</header>
{hint && <p className="text-typography-sm leading-relaxed text-muted-foreground">{hint}</p>}
<section aria-label="Recently updated workspace pages" className="min-w-0 overflow-hidden rounded-card-radius border border-floating-border bg-floating-background">
<div className="flex items-center justify-between gap-sm border-b border-floating-border px-base py-sm">
<h3 className="text-typography-sm font-semibold text-foreground">Recent work</h3>
<span className="text-typography-xs text-muted-foreground">Example data</span>
</div>
<table className="w-full table-fixed border-collapse text-left text-typography-xs">
<caption className="sr-only">Example workspace pages and their status</caption>
<thead>
<tr className="border-b border-floating-border text-muted-foreground">
<th scope="col" className="w-2/3 px-base py-sm font-medium">Page</th>
<th scope="col" className="px-base py-sm font-medium">Status</th>
</tr>
</thead>
<tbody>
{previewRows.map((row) => (
<tr className="border-b border-floating-border last:border-0" key={row.title}>
<td className="truncate px-base py-sm font-medium text-foreground">{row.title}</td>
<td className="px-base py-sm">
<span className="inline-flex max-w-full truncate rounded-tag-radius bg-muted px-tag-padding-x py-tag-padding-y text-foreground">
{row.status}
</span>
</td>
</tr>
))}
</tbody>
</table>
</section>
</article>
);
};
function PreviewExample() {
const [open, setOpen] = useState(false);
const [projectsOnly, setProjectsOnly] = useState(false);
const allCommands: CommandItemTypes[] = [
{ type: "shortcut", title: "Dashboard", shortcut: "Alt+d", Icon: <SquaresFourIcon size={20} />, hint: "Navigate to your personal dashboard.", action: () => setOpen(false) },
{ type: "shortcut", title: "Projects", shortcut: "Alt+p", Icon: <FolderIcon size={20} />, hint: "Browse and manage your projects.", action: () => setOpen(false) },
{ type: "shortcut", title: "Settings", shortcut: "Alt+s", Icon: <GearIcon size={20} />, hint: "Configure your account preferences.", action: () => setOpen(false) },
];
const commands = projectsOnly ? allCommands.filter((command) => command.type === "shortcut" && command.title === "Projects") : allCommands;
return (
<>
<Button onClick={() => setOpen(true)}>Open Commander</Button>
<CommandPalette
open={open}
commands={commands}
onChangeVisibility={setOpen}
filters={
<button
type="button"
aria-pressed={projectsOnly}
className={`rounded border border-floating-border px-3 py-1.5 text-sm ${projectsOnly ? "bg-primary/10 text-primary" : "text-foreground hover:bg-muted"}`}
onClick={() => setProjectsOnly((enabled) => !enabled)}
>
Projects only
</button>
}
Preview={CommandPreview}
/>
</>
);
}When to use Commander
Use when
You need the documented Commander interaction or presentation in a product workflow.
Avoid when
A native HTML element already provides the Commander behavior you need.
Accessibility
Keep the Commander labeled and use its documented keyboard interaction. The component preserves visible focus and communicates state without relying on color alone.
- Keep keyboard focus visible and follow the documented key commands.
- Provide an accessible name and connect descriptions or errors to the control.