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
requestAnimationFramefor visual updates; it throttles to the paint cycle. UseIntersectionObserverinstead 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 stopsRelated
- inp: fewer expensive handlers improve responsiveness.
- core-web-vitals: the metrics these techniques protect.
- react: hooks for stable callbacks.
- react-performance: render cost in depth.
- virtual-dom: fewer updates mean fewer reconciliations.