logo

Nurav UI

Community
Hooks

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)

Terminal
npx shadcn@latest add @nurav-ui/use-debounce

Alternatively, install via direct URL:

Terminal
npx shadcn@latest add https://nurav-ui.vercel.app/r/use-debounce.json

Manual Setup

Create the hook file

Copy the source into hooks/use-debounce.ts:

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

PropTypeDefaultDescription
valuerequired
TThe value you want to debounce (string, number, etc).
delay
number500The delay in milliseconds before the value updates.

Return Value

PropTypeDefaultDescription
debouncedValue
TThe debounced value of the same type as the input.

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.

Built by Varun

Last Updated:April 10, 2026

On this page