Search results page with a query field, live result count, empty state, and result cards carrying a type badge, snippet, and last updated date.
@7ovr
registry/blocks/search/2/search-block.tsx
"use client"
import * as React from "react"
import { Badge } from "@/components/ui/badge"
import { Input } from "@/components/ui/input"
import { IconPlaceholder } from "@/components/icons/icon-placeholder"
type Result = {
title: string
snippet: string
type: string
updated: string
}
const results: Result[] = [
{
title: "Getting started with Acme",
snippet:
"Install the CLI, create your first project, and ship a page in under five minutes with copy-paste blocks.",
type: "Docs",
updated: "Jun 12, 2026",
},
{
title: "Authentication and sessions",
snippet:
"Connect a provider, protect your routes, and read the current session on the server or the client.",
type: "Guide",
updated: "May 30, 2026",
},
{
title: "Theming and design tokens",
snippet:
"Every block is theme-aware. Override semantic tokens once and every component updates automatically.",
type: "Docs",
updated: "Jun 18, 2026",
},
{
title: "Deploying to production",
snippet:
"A checklist for going live: environment variables, build caching, and zero-downtime releases.",
type: "Guide",
updated: "Jun 02, 2026",
},
]
export default function SearchBlock() {
const [query, setQuery] = React.useState("")
const q = query.trim().toLowerCase()
const filtered = q
? results.filter((r) =>
`${r.title} ${r.snippet} ${r.type}`.toLowerCase().includes(q)
)
: results
return (
<section className="flex w-full items-center justify-center bg-muted/30 px-6 py-16 text-foreground">
<div className="flex w-full max-w-xl flex-col gap-5">
<div className="relative">
<IconPlaceholder
lucide="Search"
tabler="IconSearch"
hugeicons="SearchIcon"
phosphor="MagnifyingGlass"
remixicon="RiSearchLine"
className="absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground"
aria-hidden="true"
/>
<Input
type="search"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search the docs..."
aria-label="Search"
className="h-11 bg-background pl-10"
/>
</div>
<p className="text-sm text-muted-foreground tabular-nums">
{filtered.length} {filtered.length === 1 ? "Result" : "Results"}
</p>
{filtered.length === 0 ? (
<div className="rounded-lg border border-border bg-background px-4 py-12 text-center text-sm text-muted-foreground">
No results for “{query}”.
</div>
) : (
<ul className="flex flex-col gap-3">
{filtered.map((result) => (
<li
key={result.title}
className="flex flex-col gap-2 rounded-lg border border-border bg-background p-4 transition-colors hover:border-muted-foreground/40"
>
<div className="flex items-center justify-between gap-3">
<Badge variant="secondary">{result.type}</Badge>
<span className="text-xs text-muted-foreground tabular-nums">
Updated {result.updated}
</span>
</div>
<a
href="#"
className="text-sm font-semibold text-foreground hover:underline"
>
{result.title}
</a>
<p className="text-sm/relaxed text-muted-foreground">
{result.snippet}
</p>
</li>
))}
</ul>
)}
</div>
</section>
)
}