React cookbook · Beginner

Debounce a search input in React

Search an API as the user types without firing a request on every keystroke or showing stale results.

Approach

Keep the input itself instant (controlled state), and derive a debounced value with a small hook. Use the debounced value in the query key so TanStack Query fetches only after typing pauses, cancels outdated requests and caches previous searches.

Solution

import { useEffect, useState } from "react";
import { useQuery, keepPreviousData } from "@tanstack/react-query";

function useDebouncedValue<T>(value: T, delay = 300): T {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const id = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(id);
  }, [value, delay]);
  return debounced;
}

export function ProductSearch() {
  const [query, setQuery] = useState("");
  const debounced = useDebouncedValue(query.trim(), 300);

  const { data = [], isFetching } = useQuery({
    queryKey: ["products", "search", debounced],
    queryFn: ({ signal }) =>
      fetch(`/api/products?q=${encodeURIComponent(debounced)}`, { signal }).then((r) => r.json() as Promise<Product[]>),
    enabled: debounced.length >= 2,
    placeholderData: keepPreviousData,
    staleTime: 30_000,
  });

  return (
    <div role="search">
      <label htmlFor="product-search">Search products</label>
      <input id="product-search" type="search" value={query} onChange={(e) => setQuery(e.target.value)} />
      {isFetching && <span aria-live="polite">Searching…</span>}
      <ul>{data.map((p) => <li key={p.id}>{p.name}</li>)}</ul>
    </div>
  );
}

Pitfalls

  • Debouncing the input's own state (typing feels laggy).
  • Fetching in useEffect without cancellation — out-of-order responses show stale results.
  • Not encoding the query parameter.

Variations

  • Throttle instead of debounce for continuous events (scroll, resize).
  • Use useDeferredValue for client-side filtering of local data.

Further reading: TanStack Query: Query cancellation

Reference: https://tanstack.com/query/latest/docs/framework/react/guides/query-cancellation

More React recipes