import { useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { useDebounce } from '@/hooks/useDebounce'
import api from '@/services/api'
export function SearchInput() {
const [searchTerm, setSearchTerm] = useState('')
const debouncedSearchTerm = useDebounce(searchTerm, 300)
const { data: results, isLoading } = useQuery({
queryKey: ['search', debouncedSearchTerm],
queryFn: async () => {
if (!debouncedSearchTerm) return []
const { data } = await api.get('/search', {
params: { q: debouncedSearchTerm },
})
return data
},
enabled: debouncedSearchTerm.length > 2,
})
return (
<div className="relative">
<input
type="search"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search..."
className="w-full px-4 py-2 pl-10 border rounded-lg"
/>
<i className="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" />
{isLoading && (
<div className="absolute right-3 top-1/2 transform -translate-y-1/2">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-600" />
</div>
)}
{results && results.length > 0 && (
<div className="absolute top-full mt-2 w-full bg-white rounded-lg shadow-lg border max-h-96 overflow-y-auto">
{results.map((result: any) => (
<a
key={result.id}
href={`/posts/${result.id}`}
className="block px-4 py-3 hover:bg-gray-50 border-b last:border-b-0"
>
<h4 className="font-medium">{result.title}</h4>
<p className="text-sm text-gray-600">{result.excerpt}</p>
</a>
))}
</div>
)}
</div>
)
}
Search inputs that fire API requests on every keystroke create poor UX and waste server resources. Debouncing delays the search until typing pauses, reducing requests dramatically. I combine a controlled input component with a custom useDebounce hook that returns the debounced value. When the debounced value changes, React Query triggers a new search request. The input feels instantly responsive because it's controlled, while the search happens asynchronously after the debounce delay. I set the delay to 300-500ms based on input type—longer for expensive searches, shorter for simple filters. This pattern is essential for autocomplete, live search, and any input-driven queries.