Debounce waits until rapid events pause before running code, while throttle runs code at most once per interval. Debounce is for work that should happen after the user finishes, and throttle is for work that should happen steadily while events fire.
The problem: events fire fast
Typing, scrolling, and resizing fire many events in a short time. Running expensive work on every one of them, like an API request per keystroke, wastes resources. Both patterns cut down the calls, but in different ways.
Debounce vs throttle at a glance
| Aspect | Debounce | Throttle |
|---|---|---|
| When it runs | After events pause for the delay | At most once per interval |
| First event | Delayed until quiet | Runs immediately |
| Best for | Search, autosave, resize end | Scroll, drag, rapid clicks |
Debounce: wait for a pause
Debounce starts a timer on each event and resets it whenever a new event arrives. The work only runs after the events stay quiet for the delay. This is the classic search-box pattern.
import { useEffect, useState } from "react";
function useDebouncedValue(value, delay) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const id = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(id);
}, [value, delay]);
return debounced;
}The Hook keeps a second value that only updates after the input has been stable for the delay. Each change clears the previous timer, so a fast typist never sends an intermediate request.
Throttle: cap the rate
Throttle runs the work immediately, then ignores further events until the interval passes. The first call happens right away, and later calls are spaced at a fixed rate.
import { useRef } from "react";
function useThrottledCallback(callback, wait) {
const lastRun = useRef(0);
return function throttled(...args) {
const now = Date.now();
if (now - lastRun.current < wait) return;
lastRun.current = now;
callback(...args);
};
}The throttled handler records when it last ran and skips calls inside the window. It stays responsive on the first event and steady after that, which suits continuous updates like a scroll position. If events keep coming, the handler still runs only once per window, so the total work stays bounded.
Which should you use
Use debounce when only the final result matters, like a search request after typing stops. A search box that fetches results should debounce the query, because only the last keystroke matters.
Use throttle when you need continuous updates at a steady rate, like a scroll listener that updates a progress bar. If the work only matters once the user pauses, debounce is the better fit. When in doubt, ask whether a late result is still useful.
What to learn next
Both patterns build on the event handling overview, and extracting the timer logic is the same technique covered in creating a custom Hook.
Rune AI
Key Insights
- Debounce waits for a pause before running.
- Throttle runs at most once per interval.
- Debounce suits search and autosave.
- Throttle suits scroll and drag updates.
- Clear timers in cleanup to avoid leaks.
Frequently Asked Questions
When should I use debounce?
When should I use throttle?
Do I need a library for this?
Conclusion
Debounce delays work until rapid events pause, and throttle caps work to a steady rate. Pick debounce for final results and throttle for continuous updates.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.