A docs header with a fake search field that becomes the real one. Press / to open it from anywhere.
@keyframeui
registry/demos/morph-search-docs-demo.tsx
"use client";
import * as React from "react";
import { motion, useReducedMotion } from "motion/react";
import { MorphSearch, MorphSearchIcon, type MorphSearchItem } from "@/components/ui/morph-search";
const page = (
<svg aria-hidden viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round">
<path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9ZM14 3v6h6" />
</svg>
);
const heading = (
<svg aria-hidden viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round">
<path d="M4 9h16M4 15h16M10 3 8 21M16 3l-2 18" />
</svg>
);
const docs: MorphSearchItem[] = [
{ id: "introduction", group: "Getting started", label: "Introduction", description: "What Glide is, and isn't", icon: page },
{ id: "installation", group: "Getting started", label: "Installation", description: "Add Glide to Next.js or Vite", icon: page, keywords: ["install", "setup", "npm"] },
{ id: "quick-start", group: "Getting started", label: "Quick start", description: "Your first component in five minutes", icon: page },
{ id: "cli", group: "Getting started", label: "CLI", description: "Commands and flags", icon: page, keywords: ["terminal"] },
{ id: "button", group: "Components", label: "Button", description: "Actions, links and loading states", icon: page },
{ id: "dialog", group: "Components", label: "Dialog", description: "Modal windows that trap focus", icon: page, keywords: ["modal"] },
{ id: "popover", group: "Components", label: "Popover", description: "Floating panels anchored to a trigger", icon: page },
{ id: "tabs", group: "Components", label: "Tabs", description: "Switch between related views", icon: page },
{ id: "toast", group: "Components", label: "Toast", description: "Brief, non-blocking messages", icon: page, keywords: ["notification"] },
{ id: "theming", group: "Guides", label: "Theming", description: "Colors, radii and fonts", icon: page, keywords: ["css", "tokens"] },
{ id: "css-variables", group: "Guides", label: "CSS variables", description: "Theming", icon: heading, keywords: ["tokens"] },
{ id: "dark-mode", group: "Guides", label: "Dark mode", description: "Follow the system or a toggle", icon: page },
{ id: "animation", group: "Guides", label: "Animation", description: "Springs, easing and reduced motion", icon: page, keywords: ["motion"] },
{ id: "reduced-motion", group: "Guides", label: "Reduced motion", description: "Animation", icon: heading },
];
const suggested = ["installation", "dialog", "theming", "animation"].map((id) => ({
...docs.find((doc) => doc.id === id)!,
group: "Suggested",
}));
export function MorphSearchDocsDemo() {
const [doc, setDoc] = React.useState(docs[0]);
const reduceMotion = useReducedMotion();
return (
<div className="w-full max-w-3xl 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-4 border-b border-zinc-200/80 px-4 dark:border-zinc-800/80">
<span className="flex shrink-0 items-center gap-2 text-sm font-semibold text-zinc-900 dark:text-zinc-50">
<span aria-hidden className="size-5 rounded-full bg-linear-to-br from-sky-400 to-emerald-400" />
Glide
<span className="rounded-full bg-zinc-100 px-1.5 py-0.5 font-mono text-[10px] font-normal text-zinc-500 dark:bg-zinc-900">
v3
</span>
</span>
<MorphSearch
variant="palette"
hotkey="/"
items={docs}
suggestions={suggested}
placeholder="Search documentation…"
onSelect={(item) => setDoc(item)}
className="mx-auto h-9 w-full max-w-64 justify-start gap-2.5 rounded-lg bg-zinc-50 px-2.5 dark:bg-zinc-900/60"
>
<MorphSearchIcon />
<span className="truncate">Search docs…</span>
<kbd className="ml-auto rounded-[5px] bg-background px-1.5 py-0.5 font-mono text-[10.5px] ring-1 ring-foreground/10">
/
</kbd>
</MorphSearch>
<span className="hidden shrink-0 text-sm text-zinc-500 sm:block">GitHub</span>
</header>
<motion.article
key={doc.id}
initial={{ opacity: 0, y: reduceMotion ? 0 : 6 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, ease: "easeOut" }}
className="px-6 pt-10 pb-14 sm:px-10"
>
<p className="text-xs text-zinc-500">
Docs <span className="px-1 text-zinc-300 dark:text-zinc-700">/</span> {doc.group}
</p>
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-zinc-900 dark:text-zinc-50">
{doc.label}
</h2>
<p className="mt-2 text-sm text-zinc-500">{doc.description}.</p>
<div className="mt-8 space-y-2.5">
{["w-full", "w-11/12", "w-4/5", "w-2/3"].map((line) => (
<span key={line} className={`block h-2.5 rounded-full bg-zinc-100 dark:bg-zinc-900 ${line}`} />
))}
</div>
<p className="mt-10 font-mono text-xs text-zinc-500">Press / to search.</p>
</motion.article>
</div>
);
}
Live preview, examples and props: https://keyframeui.com/components/morph-search#morph-search-docs-demo



























