Shreyansh Jha

Modern React applications often make API requests while users type into search boxes. Without optimization, every keystroke triggers a request, causing unnecessary network traffic and poor performance.

A custom debounce hook solves this problem by delaying execution until the user stops typing for a specified amount of time.

In this guide, you'll learn:

  • What debouncing is
  • Why it's useful
  • How to build a reusable useDebounce hook
  • How to use it in real-world applications
  • Common mistakes and best practices

What is Debouncing?

Debouncing is a technique that delays executing a function until a certain amount of time has passed since the last event occurred.

For example:

Without debouncing:
H
He
Hel
Hell
Hello

Every keystroke sends an API request.

With debouncing (500ms):
Typing...
Typing...
Typing...
User Stops

Single API Request

Instead of sending five requests, only one request is made after the user finishes typing.

Why Use a Custom Debounce Hook?

  • A reusable debounce hook provides several advantages:
  • Reduces unnecessary API calls
  • Improves application performance
  • Creates a better user experience
  • Prevents backend overload
  • Keeps React components clean and reusable

Creating the Custom Hook
Create a new file:

src/hooks/useDebounce.js

JavaScript Version

import { useEffect, useState } from "react";

function useDebounce(value, delay = 500) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(timer);
  }, [value, delay]);

  return debouncedValue;
}

export default useDebounce;

Enter fullscreen mode Exit fullscreen mode

TypeScript Version

import { useEffect, useState } from "react";

function useDebounce<T>(value: T, delay: number = 500): T {
  const [debouncedValue, setDebouncedValue] = useState<T>(value);

  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(timer);
  }, [value, delay]);

  return debouncedValue;
}

export default useDebounce;

Enter fullscreen mode Exit fullscreen mode

Understanding the Hook

Let's break it down.

1. Store the Debounced Value
const [debouncedValue, setDebouncedValue] = useState(value);
Initially, it stores the current value.

2. Wait Before Updating
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);

Instead of updating immediately, React waits for the specified delay.

3. Clear Previous Timer
return () => clearTimeout(timer);
If the user types again before the timer finishes, the previous timeout is cancelled.
Only the final value gets stored.

Using the Hook
Suppose we have a search component.

import { useState, useEffect } from "react";
import useDebounce from "./hooks/useDebounce";

function Search() {
  const [query, setQuery] = useState("");

  const debouncedQuery = useDebounce(query, 500);

  useEffect(() => {
    if (!debouncedQuery) return;

    console.log("Fetching:", debouncedQuery);

    // Fetch API
  }, [debouncedQuery]);

  return (
    <input
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      placeholder="Search..."
    />
  );
}

export default Search;

Enter fullscreen mode Exit fullscreen mode

How It Works
Suppose the user types:
R
Re
Rea
Reac
React

Without debounce:
API Call
API Call
API Call
API Call
API Call

With debounce:
Typing...
(wait 500ms)

API Call (React)

Only one request is sent.

When Should You Use Debouncing?

  • Use debouncing when:
  • Searching from an API
  • Filtering large datasets
  • Autocomplete inputs
  • Saving drafts automatically
  • Performing expensive calculations

Avoid it for:

  • Button clicks
  • Form submissions
  • Immediate UI updates

Conclusion
A custom useDebounce hook is one of the most useful utilities you can add to your React toolkit. It helps reduce unnecessary API calls, improves application performance, and provides a smoother user experience.
By encapsulating the debounce logic inside a reusable hook, your components remain clean, maintainable, and easy to test. Whether you're building search bars, dashboards, filters, or AI-powered applications, useDebounce is a simple optimization that delivers significant performance benefits.