A search button in a site header. It grows into a field with popular pages underneath; pick one to go there.
@keyframeui
registry/demos/morph-search-demo.tsx
"use client";
import * as React from "react";
import { motion, useReducedMotion } from "motion/react";
import { MorphSearch, 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 pages: MorphSearchItem[] = [
{ id: "pricing", group: "Pages", label: "Pricing", description: "Plans for every team", icon: glyph("M20.6 13.4l-7.2 7.2a2 2 0 0 1-2.8 0L3 13V3h10l7.6 7.6a2 2 0 0 1 0 2.8ZM7.5 7.5h.01"), keywords: ["plans", "cost"] },
{ id: "changelog", group: "Pages", label: "Changelog", description: "What shipped this week", icon: glyph("M12 3l1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9Z"), keywords: ["updates", "release"] },
{ id: "customers", group: "Pages", label: "Customers", description: "Stories from teams like yours", 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 8M22 21v-2a4 4 0 0 0-3-3.9M16 3.1a4 4 0 0 1 0 7.8") },
{ id: "careers", group: "Pages", label: "Careers", description: "We're hiring", icon: glyph("M3 7h18v13H3zM8 7V4h8v3M3 13h18"), keywords: ["jobs"] },
{ id: "analytics", group: "Products", label: "Analytics", description: "Every event, in real time", icon: glyph("M3 3v18h18M7 14l4-4 3 3 6-6") },
{ id: "payments", group: "Products", label: "Payments", description: "Cards, wallets and payouts", icon: glyph("M2 6h20v12H2zM2 10h20M6 15h4"), keywords: ["billing", "checkout"] },
{ id: "identity", group: "Products", label: "Identity", description: "Sign-in that just works", icon: glyph("M12 3l8 3v6c0 5-3.5 8-8 9-4.5-1-8-4-8-9V6z"), keywords: ["auth", "login", "sso"] },
{ id: "storage", group: "Products", label: "Storage", description: "Files at the edge", icon: glyph("M4 6c0-1.7 3.6-3 8-3s8 1.3 8 3-3.6 3-8 3-8-1.3-8-3ZM4 6v12c0 1.7 3.6 3 8 3s8-1.3 8-3V6M4 12c0 1.7 3.6 3 8 3s8-1.3 8-3") },
{ id: "sales", group: "Help", label: "Contact sales", description: "Talk to a human", icon: glyph("M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2Z"), keywords: ["enterprise"] },
{ id: "status", group: "Help", label: "System status", description: "All systems normal", icon: glyph("M22 12h-4l-3 9L9 3l-3 9H2"), keywords: ["uptime", "outage"] },
];
const popular = ["pricing", "analytics", "changelog", "sales"].map((id) => ({
...pages.find((page) => page.id === id)!,
group: "Popular",
}));
export function MorphSearchDemo() {
const [page, setPage] = React.useState(pages[0]);
const reduceMotion = useReducedMotion();
return (
<div className="w-full max-w-2xl overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-950">
<header className="flex h-14 items-center gap-6 border-b border-zinc-200/80 px-4 dark:border-zinc-800/80">
<span className="flex items-center gap-2 text-sm font-semibold text-zinc-900 dark:text-zinc-50">
<span aria-hidden className="size-5 rounded-md bg-zinc-900 dark:bg-zinc-100" />
Northwind
</span>
<nav className="hidden gap-5 text-sm text-zinc-500 sm:flex">
{["Products", "Pricing", "Customers", "Docs"].map((link) => (
<span key={link}>{link}</span>
))}
</nav>
<div className="ml-auto flex items-center gap-3">
<MorphSearch
items={pages}
suggestions={popular}
placeholder="Search Northwind…"
onSelect={(item) => setPage(item)}
/>
<span aria-hidden className="size-8 rounded-full bg-linear-to-br from-amber-300 to-rose-400" />
</div>
</header>
<motion.main
key={page.id}
initial={{ opacity: 0, y: reduceMotion ? 0 : 6 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, ease: "easeOut" }}
className="px-6 pt-12 pb-16"
>
<p className="font-mono text-xs uppercase tracking-widest text-zinc-500">{page.group}</p>
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-zinc-900 dark:text-zinc-50">
{page.label}
</h2>
<p className="mt-2 max-w-sm text-sm leading-relaxed text-zinc-500">{page.description}.</p>
<div className="mt-8 grid grid-cols-3 gap-3">
{[0, 1, 2].map((block) => (
<span key={block} className="h-16 rounded-lg bg-zinc-100 dark:bg-zinc-900" />
))}
</div>
</motion.main>
</div>
);
}
Live preview, examples and props: https://keyframeui.com/components/morph-search#morph-search-demo



























