modals /
Command Palette (⌘K)
Keyboard-driven command palette with grouped results, shortcuts, and a ⌘K trigger. Built with cmdk + Radix Dialog.
Preview
Source
tsx
"use client";
import { useState, useEffect, useCallback } from "react";
import { Command } from "cmdk";
import {
Dialog,
DialogContent,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import {
Search,
Home,
FileText,
BarChart2,
Settings,
Users,
Mail,
Plus,
ExternalLink,
} from "lucide-react";
const actions = [
{ group: "Pages", icon: Home, label: "Go to Dashboard", shortcut: "G D" },
{ group: "Pages", icon: FileText, label: "View case studies", shortcut: "G C" },
{ group: "Pages", icon: BarChart2, label: "Open analytics", shortcut: "G A" },
{ group: "Actions", icon: Plus, label: "Create new project", shortcut: "N P" },
{ group: "Actions", icon: Mail, label: "Send email report", shortcut: "" },
{ group: "Actions", icon: Users, label: "Invite team member", shortcut: "" },
{ group: "Settings", icon: Settings, label: "Account settings", shortcut: "" },
{ group: "Settings", icon: ExternalLink, label: "Open documentation", shortcut: "" },
];
const groups = ["Pages", "Actions", "Settings"] as const;
export default function CommandPalette() {
const [open, setOpen] = useState(false);
const toggle = useCallback(() => setOpen((v) => !v), []);
useEffect(() => {
function onKeyDown(e: KeyboardEvent) {
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
e.preventDefault();
toggle();
}
}
document.addEventListener("keydown", onKeyDown);
return () => document.removeEventListener("keydown", onKeyDown);
}, [toggle]);
return (
<div className="flex min-h-[200px] flex-col items-center justify-center gap-4 p-8">
<Button
variant="outline"
onClick={toggle}
className="w-full max-w-xs justify-between text-muted-foreground"
>
<span className="flex items-center gap-2">
<Search className="h-4 w-4" />
Search or jump to…
</span>
<kbd className="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border border-border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground">
<span className="text-xs">⌘</span>K
</kbd>
</Button>
<p className="text-xs text-muted-foreground">
Or press <kbd className="font-mono text-foreground">⌘K</kbd> anywhere
</p>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="overflow-hidden p-0 shadow-2xl sm:max-w-lg">
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
<div className="flex items-center border-b border-border px-3">
<Search className="mr-2 h-4 w-4 shrink-0 text-muted-foreground" />
<Command.Input
placeholder="Search actions, pages, settings…"
className="flex h-12 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50"
/>
</div>
<Command.List className="max-h-80 overflow-y-auto overflow-x-hidden p-2">
<Command.Empty className="py-8 text-center text-sm text-muted-foreground">
No results found.
</Command.Empty>
{groups.map((group) => {
const items = actions.filter((a) => a.group === group);
return (
<Command.Group
key={group}
heading={group}
className="[&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-wide [&_[cmdk-group-heading]]:text-muted-foreground"
>
{items.map(({ icon: Icon, label, shortcut }) => (
<Command.Item
key={label}
value={label}
onSelect={() => setOpen(false)}
className="flex cursor-pointer items-center justify-between gap-2 rounded-lg px-2 py-2 text-sm text-foreground aria-selected:bg-muted"
>
<span className="flex items-center gap-2">
<Icon className="h-4 w-4 text-muted-foreground" />
{label}
</span>
{shortcut && (
<kbd className="pointer-events-none ml-auto inline-flex h-5 select-none items-center gap-1 rounded border border-border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground">
{shortcut}
</kbd>
)}
</Command.Item>
))}
</Command.Group>
);
})}
</Command.List>
</Command>
</DialogContent>
</Dialog>
</div>
);
} Claude Code Instructions
CLI Install
npx innovations add command-paletteWhere to use it
Add this to your root layout so ⌘K opens globally on every page.
In Next.js root layout (app/layout.tsx):
import CommandPalette from '@/components/innovations/modals/command-palette';
// Render inside <body> at the end
In Astro root layout:
import CommandPalette from '../components/innovations/modals/command-palette';
<CommandPalette client:load />
The useEffect keydown listener is SSR-guarded (runs only in the browser).
Extend the actions array with your real routes, features, and settings.