useDebounce
A performance-oriented hook that delays the update of a value until a specific time has passed — ideal for search inputs and API optimization.Copy Markdown
Overview
useDebounce prevents a value from being updated too frequently. It waits for a specified "cooldown" period of inactivity before committing the latest value. This is critical for preventing "API thrashing" where a request is sent on every single keystroke.
Commonly used for:
- Search inputs that fetch data from a server
- Expensive UI calculations based on input
- Handling window resize or scroll events efficiently
- Preventing rapid-fire button clicks (simple usage)
Installation
Automatic (CLI)
npx shadcn@latest add @nurav-ui/use-debounceAlternatively, install via direct URL:
npx shadcn@latest add https://nurav-ui.vercel.app/r/use-debounce.jsonManual Setup
Create the hook file
Copy the source into hooks/use-debounce.ts:
import { useState, useEffect } from "react";
export function useDebounce<T>(value: T, delay: number = 500): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}Usage
Search Input Example
The most common use case: fetching data only after the user stops typing.
import { useState, useEffect } from 'react';
import { useDebounce } from '@/hooks/use-debounce';
export default function SearchBar() {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearch = useDebounce(searchTerm, 500);
useEffect(() => {
if (debouncedSearch) {
console.log(`Fetching results for: ${debouncedSearch}`);
// perform your API call here
}
}, [debouncedSearch]);
return (
<input
type="text"
placeholder="Search..."
className="border px-4 py-2 rounded-md"
onChange={(e) => setSearchTerm(e.target.value)}
/>
);
}Window Resize Example
Handling high-frequency events like resizing without lagging the browser.
import { useState, useEffect } from 'react';
import { useDebounce } from '@/hooks/use-debounce';
export default function ResizeMonitor() {
const [width, setWidth] = useState(0);
const debouncedWidth = useDebounce(width, 300);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return (
<div className="p-4 bg-muted rounded-lg">
<p>Current (Raw) Width: {width}px</p>
<p>Debounced Width: {debouncedWidth}px</p>
<span className="text-xs italic">Debounced value updates 300ms after you stop resizing.</span>
</div>
);
}Form Validation Example
Validating a username availability in real-time without overwhelming the database.
import { useState, useEffect } from 'react';
import { useDebounce } from '@/hooks/use-debounce';
export default function UsernameInput() {
const [username, setUsername] = useState('');
const debouncedUsername = useDebounce(username, 700);
const [isValidating, setIsValidating] = useState(false);
useEffect(() => {
if (debouncedUsername.length >= 3) {
setIsValidating(true);
// Mock API call
setTimeout(() => setIsValidating(false), 500);
}
}, [debouncedUsername]);
return (
<div>
<input
value={username}
onChange={(e) => setUsername(e.target.value)}
className="border p-2"
/>
{isValidating && <p className="text-sm text-blue-500">Checking availability...</p>}
</div>
);
}API Reference
Parameters
Return Value
Performance Note: useDebounce uses setTimeout internally. It is highly efficient because it resets the timer on every keystroke, ensuring zero performance overhead during active typing.