Components
Advanced Search Bar
Advanced Search Bar
Intelligent search with autocomplete, trending suggestions, and instant results with performance metrics.
1.2k results
0.3s response
Installation
1
Install the packages
npm i motion clsx tailwind-merge2
Add util file
lib/util.ts
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
3
Copy and paste the following code into your project
advanced-search-bar.tsx
"use client";
import { motion } from "motion/react";
import { useState, useEffect } from "react";
import { cn } from "@/lib/utils";
import { FiSearch, FiTrendingUp } from "react-icons/fi";
type AdvancedSearchBarProps = {
placeholder?: string;
debounce?: number;
};
const AdvancedSearchBar = ({
placeholder = "Search...",
debounce = 300,
}: AdvancedSearchBarProps) => {
const [query, setQuery] = useState("");
const [debouncedQuery, setDebouncedQuery] = useState("");
const [isFocused, setIsFocused] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedQuery(query);
}, debounce);
return () => clearTimeout(timer);
}, [query, debounce]);
const suggestions = [
"React components",
"UI animations",
"Tailwind CSS",
"Framer Motion",
];
return (
<div className="w-full max-w-md mx-auto">
<div className="relative">
<div className={cn(
"relative flex items-center bg-white dark:bg-neutral-900 border rounded-lg transition-all duration-200",
isFocused ? "border-blue-500 shadow-lg" : "border-neutral-300 dark:border-neutral-700"
)}>
<FiSearch className="ml-3 text-neutral-400" />
<input
type="text"
placeholder={placeholder}
value={query}
onChange={(e) => setQuery(e.target.value)}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
className="flex-1 px-3 py-3 bg-transparent outline-none text-neutral-900 dark:text-white placeholder-neutral-500"
/>
{debouncedQuery && (
<motion.div
className="mr-3 text-xs text-neutral-500"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
>
{debouncedQuery.length} chars
</motion.div>
)}
</div>
{/* Suggestions dropdown */}
{isFocused && query.length === 0 && (
<motion.div
className="absolute top-full mt-2 w-full bg-white dark:bg-neutral-900 border border-neutral-300 dark:border-neutral-700 rounded-lg shadow-lg z-10"
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
>
<div className="p-2">
<div className="flex items-center gap-2 px-3 py-2 text-xs text-neutral-500">
<FiTrendingUp />
Trending searches
</div>
{suggestions.map((suggestion, index) => (
<motion.button
key={suggestion}
className="w-full text-left px-3 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded"
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
onClick={() => setQuery(suggestion)}
>
{suggestion}
</motion.button>
))}
</div>
</motion.div>
)}
</div>
</div>
);
};
export default AdvancedSearchBar;"use client";
import { motion } from "motion/react";
import { useState, useEffect } from "react";
import { cn } from "@/lib/utils";
import { FiSearch, FiTrendingUp } from "react-icons/fi";
type AdvancedSearchBarProps = {
placeholder?: string;
debounce?: number;
};
const AdvancedSearchBar = ({
placeholder = "Search...",
debounce = 300,
}: AdvancedSearchBarProps) => {
const [query, setQuery] = useState("");
const [debouncedQuery, setDebouncedQuery] = useState("");
const [isFocused, setIsFocused] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedQuery(query);
}, debounce);
return () => clearTimeout(timer);
}, [query, debounce]);
const suggestions = [
"React components",
"UI animations",
"Tailwind CSS",
"Framer Motion",
];
return (
<div className="w-full max-w-md mx-auto">
<div className="relative">
<div className={cn(
"relative flex items-center bg-white dark:bg-neutral-900 border rounded-lg transition-all duration-200",
isFocused ? "border-blue-500 shadow-lg" : "border-neutral-300 dark:border-neutral-700"
)}>
<FiSearch className="ml-3 text-neutral-400" />
<input
type="text"
placeholder={placeholder}
value={query}
onChange={(e) => setQuery(e.target.value)}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
className="flex-1 px-3 py-3 bg-transparent outline-none text-neutral-900 dark:text-white placeholder-neutral-500"
/>
{debouncedQuery && (
<motion.div
className="mr-3 text-xs text-neutral-500"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
>
{debouncedQuery.length} chars
</motion.div>
)}
</div>
{/* Suggestions dropdown */}
{isFocused && query.length === 0 && (
<motion.div
className="absolute top-full mt-2 w-full bg-white dark:bg-neutral-900 border border-neutral-300 dark:border-neutral-700 rounded-lg shadow-lg z-10"
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
>
<div className="p-2">
<div className="flex items-center gap-2 px-3 py-2 text-xs text-neutral-500">
<FiTrendingUp />
Trending searches
</div>
{suggestions.map((suggestion, index) => (
<motion.button
key={suggestion}
className="w-full text-left px-3 py-2 text-sm hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded"
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
onClick={() => setQuery(suggestion)}
>
{suggestion}
</motion.button>
))}
</div>
</motion.div>
)}
</div>
</div>
);
};
export default AdvancedSearchBar;4
Update the import paths to match your project setup
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| placeholder | string | Search... | The placeholder text for the search input. |
| debounce | number | 300 | Debounce delay in milliseconds for search input. |