Overview

Debouncing delays a function until a quiet period has passed since the last call; every new call resets the timer, so a continuous stream never fires it. Throttling caps a function at one run per interval: it fires on the first call, then ignores calls until the interval ends. Debounce when only the final value matters (search-as-you-type, autosave, validation on change). Throttle when the UI must keep updating during the stream (scroll position, pointer move, resize). Both cut wasted work that hurts inp.

Rules

  • Prefer requestAnimationFrame for visual updates; it throttles to the paint cycle. Use IntersectionObserver instead of a scroll handler when you only need visibility.
  • In React, keep the debounced function stable across renders and clear the pending timer on unmount. The useDebounce(value, delay) hook below debounces a value instead of a callback.
  • Libraries: _.debounce(fn, ms) and _.throttle(fn, ms) from lodash.
  • Server-side rate limiting is a different mechanism: it limits requests per client per time window.
function useDebounce(value, delay) {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);
  return debounced;
}
const query = useDebounce(inputValue, 300); // the API call fires 300 ms after typing stops