An app's command menu. The trigger travels to the middle of the screen, with grouped commands and their shortcuts. Opens with ⌘K.
@keyframeui
registry/demos/morph-search-command-demo.tsx
"use client";
import * as React from "react";
import { AnimatePresence, motion } from "motion/react";
import { MorphSearch, MorphSearchIcon, type MorphSearchItem } from "@/components/ui/morph-search";
const glyph = (d: string) => (
<svg aria-hidden viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round">
<path d={d} />
</svg>
);
const useIsMac = () =>
React.useSyncExternalStore(
() => () => {},
() => /Mac|iPhone|iPad/.test(navigator.platform),
() => true,
);
function commands(mod: string): MorphSearchItem[] {
return [
{ id: "new-project", group: "Suggestions", label: "Create new project", icon: glyph("M12 5v14M5 12h14"), shortcut: [mod, "N"] },
{ id: "invite", group: "Suggestions", label: "Invite teammate", icon: glyph("M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8M19 8v6M22 11h-6"), shortcut: [mod, "I"] },
{ id: "deploy", group: "Suggestions", label: "Deploy to production", icon: glyph("M13 2 3 14h9l-1 8 10-12h-9l1-8z"), keywords: ["ship", "release"] },
{ id: "dashboard", group: "Go to", label: "Dashboard", icon: glyph("M3 3h7v7H3zM14 3h7v7h-7zM3 14h7v7H3zM14 14h7v7h-7z"), shortcut: ["G", "D"] },
{ id: "projects", group: "Go to", label: "Projects", icon: glyph("M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z"), shortcut: ["G", "P"] },
{ id: "billing", group: "Go to", label: "Billing", icon: glyph("M2 6h20v12H2zM2 10h20M6 15h4"), shortcut: ["G", "B"], keywords: ["invoices", "plan"] },
{ id: "settings", group: "Go to", label: "Settings", icon: glyph("M4 6h9M17 6h3M4 12h3M11 12h9M4 18h11M19 18h1M15 4v4M9 10v4M17 16v4"), shortcut: [mod, ","], keywords: ["preferences"] },
{ id: "light", group: "Theme", label: "Light", icon: glyph("M12 16a4 4 0 1 0 0-8 4 4 0 0 0 0 8ZM12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"), keywords: ["appearance"] },
{ id: "dark", group: "Theme", label: "Dark", icon: glyph("M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"), keywords: ["appearance", "night"] },
{ id: "system", group: "Theme", label: "Match system", icon: glyph("M3 4h18v12H3zM8 20h8M12 16v4"), keywords: ["appearance", "auto"] },
{ id: "log-out", group: "Account", label: "Log out", icon: glyph("M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9"), keywords: ["sign out"] },
];
}
export function MorphSearchCommandDemo() {
const mod = useIsMac() ? "⌘" : "Ctrl";
const items = React.useMemo(() => commands(mod), [mod]);
const [ran, setRan] = React.useState<{ label: string; at: number } | null>(null);
React.useEffect(() => {
if (!ran) return;
const timer = window.setTimeout(() => setRan(null), 2400);
return () => window.clearTimeout(timer);
}, [ran]);
return (
<div className="relative flex h-[26rem] w-full max-w-3xl overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-950">
<aside className="hidden w-48 shrink-0 flex-col gap-1 border-r border-zinc-200/80 p-3 sm:flex dark:border-zinc-800/80">
<span className="mb-3 flex items-center gap-2 px-2 text-sm font-semibold text-zinc-900 dark:text-zinc-50">
<span aria-hidden className="size-5 rounded-md bg-orange-500" />
Acme
</span>
{["Dashboard", "Projects", "Deployments", "Billing", "Settings"].map((link, i) => (
<span
key={link}
className={
i === 1
? "rounded-md bg-zinc-100 px-2 py-1.5 text-sm text-zinc-900 dark:bg-zinc-900 dark:text-zinc-50"
: "px-2 py-1.5 text-sm text-zinc-500"
}
>
{link}
</span>
))}
</aside>
<div className="flex min-w-0 flex-1 flex-col">
<header className="flex h-14 items-center justify-between gap-4 border-b border-zinc-200/80 px-4 dark:border-zinc-800/80">
<p className="truncate text-sm text-zinc-500">
Acme <span className="px-1 text-zinc-300 dark:text-zinc-700">/</span>
<span className="text-zinc-900 dark:text-zinc-50">Projects</span>
</p>
<MorphSearch
variant="palette"
hotkey="k"
items={items}
placeholder="Type a command or search…"
onSelect={(item) => setRan({ label: item.label, at: Date.now() })}
className="h-8 gap-2 rounded-lg px-2 text-[13px]"
>
<MorphSearchIcon className="size-3.5" />
<span>Commands</span>
<kbd className="ml-1 rounded-[5px] bg-foreground/[0.06] px-1.5 py-0.5 font-mono text-[10.5px]">
{mod === "⌘" ? "⌘K" : "Ctrl K"}
</kbd>
</MorphSearch>
</header>
<div className="grid flex-1 grid-cols-2 content-start gap-3 p-4 sm:grid-cols-3">
{["Marketing site", "Mobile app", "Docs", "Billing service", "Design system", "Status page"].map((name) => (
<div key={name} className="rounded-lg border border-zinc-200/80 p-3 dark:border-zinc-800/80">
<span aria-hidden className="block size-6 rounded-md bg-zinc-100 dark:bg-zinc-900" />
<p className="mt-6 truncate text-sm font-medium text-zinc-900 dark:text-zinc-50">{name}</p>
<p className="text-xs text-zinc-500">Updated today</p>
</div>
))}
</div>
</div>
<AnimatePresence>
{ran ? (
<motion.p
key={ran.at}
role="status"
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 4 }}
transition={{ duration: 0.2, ease: "easeOut" }}
className="absolute bottom-4 left-1/2 flex -translate-x-1/2 items-center gap-2 rounded-full bg-zinc-900 px-3.5 py-2 text-xs text-zinc-50 shadow-lg dark:bg-zinc-50 dark:text-zinc-900"
>
<svg aria-hidden viewBox="0 0 24 24" className="size-3.5" fill="none" stroke="currentColor" strokeWidth={2.25} strokeLinecap="round" strokeLinejoin="round">
<path d="M5 12.5l4.5 4.5L19 7.5" />
</svg>
{ran.label}
</motion.p>
) : null}
</AnimatePresence>
</div>
);
}
Live preview, examples and props: https://keyframeui.com/components/morph-search#morph-search-command-demo



























