Published on

useDebounce Hook in React

Authors

The Problem

When users type in a search box, you don't want to fire an API call on every keystroke. That's wasteful and slow.

The Solution

Here's a tiny useDebounce hook that delays a value update until the user stops typing:

import { useState, useEffect } from 'react'

export 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
}

Usage Example

function SearchBar() {
  const [query, setQuery] = useState('')
  const debouncedQuery = useDebounce(query, 300)

  useEffect(() => {
    if (debouncedQuery) {
      fetch(`/api/search?q=${debouncedQuery}`)
    }
  }, [debouncedQuery])

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

#HTML Sample Code

<!DOCTYPE html>
<html>
  <head>
    <title>Page Title</title>
  </head>
  <body>
    <h1>This is a Heading</h1>
    <p>This is a paragraph.</p>
  </body>
</html>

SQL Sample Code

SELECT u.id, u.name, COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE u.created_at >= '2026-01-01'
GROUP BY u.id, u.name
HAVING COUNT(o.id) > 5
ORDER BY order_count DESC
LIMIT 10;

Python Sample Code

from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci(n: int) -> int:
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

if __name__ == "__main__":
    for i in range(10):
        print(f"fib({i}) = {fibonacci(i)}")

Go Sample Code

package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	ch := make(chan int, 5)

	for i := 1; i <= 5; i++ {
		wg.Add(1)
		go func(n int) {
			defer wg.Done()
			ch <- n * n
		}(i)
	}

	wg.Wait()
	close(ch)

	for v := range ch {
		fmt.Println(v)
	}
}

Rust Sample Code

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    let sum: i32 = numbers.iter().filter(|&&x| x % 2 == 0).sum();
    println!("Sum of evens: {}", sum);
}

Bash Sample Code

#!/usr/bin/env bash
set -euo pipefail

for file in *.log; do
  lines=$(wc -l < "$file")
  echo "$file has $lines lines"
done

JSON Sample

{
  "name": "useDebounce",
  "version": "1.0.0",
  "keywords": ["react", "hooks", "debounce"],
  "peerDependencies": {
    "react": ">=17.0.0"
  }
}

CSS Sample Code

.search-input {
  width: 100%;
  padding: 0.75rem 1rem;
  border: 1px solid #d1d5db;
  border-radius: 0.5rem;
  transition: border-color 150ms ease-in-out;
}

.search-input:focus {
  outline: none;
  border-color: #3b82f6;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.25);
}

Why This Works

The useEffect clears the previous timer every time value changes, so the API call only fires after the user pauses typing for 300ms.

Drop this into any project — it'll save you from rate limits and improve UX instantly.