A user types into a search box:
R
Re
Rea
Reac
React
If your application sends an API request for every keystroke, that's 5 requests for a single search.
Now consider scrolling through a webpage. The browser can fire scroll events continuously while the user moves.
Should your application run expensive logic for every single event?
Probably not.
This is where two simple but extremely useful techniques come in:
Debouncing and Throttling.
They both help control how often a function executes, but they solve different problems.
The easiest way to remember them is:
Debounce waits for the activity to stop. Throttle limits how often the activity is processed.
Let's see what that actually means.
The Problem: Events Can Happen Too Fast
Modern applications react to events constantly:
Keyboard input
Mouse movement
Scrolling
Window resizing
API responses
Button clicks
Some of these events can happen much faster than your application needs to process them.
For example, a user typing:
React
might generate:
R → event
Re → event
Rea → event
Reac → event
React → event
But perhaps you only want to search after the user has finished typing.
That's a perfect use case for debouncing.
On the other hand, when the user scrolls, you may want updates to happen continuously — just not hundreds of times per second.
That's where throttling helps.
What Is Debouncing?
Debouncing means:
Don't execute the function until the user stops triggering the event for a specified amount of time.
Imagine a search box with a debounce delay of 300ms.
The user types:
R
Timer starts.
Then:
Re
The timer resets.
Then:
Rea
Timer resets again.
This continues until the user stops typing.
Finally:
React
↓
300ms without typing
↓
API request
Instead of:
R → API
Re → API
Rea → API
Reac → API
React → API
you get:
React → API
That's the core idea behind debouncing.
A Simple Debounce Implementation
function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
fn(...args);
}, delay);
};
}
Now:
const search = debounce((query) => {
console.log("Searching:", query);
}, 300);
If we call:
search("R");
search("Re");
search("Rea");
search("Reac");
search("React");
the previous timer keeps getting cancelled.
Only after 300ms of inactivity does the function execute.
Where Is Debouncing Useful?
Debouncing works best when intermediate events don't matter and you mainly care about the final action.
Search
User types
↓
Wait
↓
Search
Autosave
User edits document
↓
Wait for pause
↓
Save
Form validation
User types
↓
Wait
↓
Validate
Filtering
User changes filters
↓
Wait
↓
Perform expensive filtering
The common pattern is:
Event Event Event Event
↓
STOP
↓
Execute
What Is Throttling?
Throttling takes a different approach.
Instead of waiting for the activity to stop, throttling says:
Execute the function at most once during a specific time interval.
Suppose we throttle a function to once every 200ms.
The browser might generate:
scroll scroll scroll scroll scroll scroll scroll
But our application processes it like:
0ms → Execute
50ms → Ignore
100ms → Ignore
150ms → Ignore
200ms → Execute
250ms → Ignore
300ms → Ignore
400ms → Execute
The event is still happening continuously.
We're simply controlling how frequently our expensive logic runs.
A Simple Throttle Implementation
function throttle(fn, delay) {
let lastCall = 0;
return (...args) => {
const now = Date.now();
if (now - lastCall >= delay) {
lastCall = now;
fn(...args);
}
};
}
For example:
const handleScroll = throttle(() => {
console.log("Processing scroll...");
}, 200);
window.addEventListener("scroll", handleScroll);
Even if the browser fires many scroll events, our function runs at a controlled frequency.
Where Is Throttling Useful?
Throttling is useful when you do want updates while the activity is happening, but you don't need to process every event.
Common examples include:
Scroll events
Scroll
↓
Periodic updates
Mouse movement
Mouse moves
↓
Process position periodically
Window resizing
Resize
↓
Recalculate periodically
Dragging
Drag
↓
Update position at a controlled rate
The pattern is:
Event Event Event Event Event Event
↓ ↓ ↓
Run Run Run
Debounce vs Throttle
Here's the simplest comparison:
| Debouncing | Throttling | |
|---|---|---|
| Main idea | Wait for activity to stop | Limit execution frequency |
| During continuous activity | Doesn't execute yet | Executes periodically |
| Search box | ✅ Great fit | Usually unnecessary |
| Scroll | Usually unnecessary | ✅ Great fit |
| Autosave | ✅ Great fit | Sometimes |
| Mouse movement | Usually unnecessary | ✅ Great fit |
| Window resize | Sometimes | ✅ Common |
| Final event matters | ✅ | Not necessarily |
The mental model is more important than memorizing the table.
Debounce
Activity
Activity
Activity
Activity
↓
STOP
↓
WAIT
↓
EXECUTE
Throttle
Activity Activity Activity Activity Activity
↓ ↓ ↓
EXECUTE EXECUTE EXECUTE
A Real Search Example
Imagine you're building an e-commerce application.
The search box receives:
wireless headphones
Without debouncing, the application might make a request for almost every character:
w
wi
wir
wire
wirel
wirele
...
wireless headphones
That's a lot of unnecessary work.
With debouncing:
User types
↓
Timer resets after every keystroke
↓
User stops typing
↓
300ms passes
↓
Search API
The application waits for a meaningful pause before doing the expensive operation.
This isn't just about saving API calls.
It can also mean:
- Less server work
- Less network traffic
- Fewer unnecessary renders
- Better user experience
A Real Scroll Example
Now consider a dashboard where you want to update something based on scroll position.
The browser may produce many events while scrolling:
scroll
scroll
scroll
scroll
scroll
scroll
scroll
...
Waiting for the user to stop scrolling isn't what we want.
We want the application to keep responding while scrolling.
But processing every event may be unnecessarily expensive.
So we throttle:
Scroll events
↓
Throttle
↓
Process every 100–200ms
↓
Update UI
That's why throttling is generally a better fit for continuous interactions.
The Question You Should Ask
When you encounter a rapidly firing event, don't start by asking:
"Should I use debounce?"
Instead ask:
Do I care about the final action?
If yes:
→ Debounce
For example:
Search
Autosave
Validation
Do I need periodic updates while the activity continues?
If yes:
→ Throttle
For example:
Scroll
Mouse movement
Dragging
Resize
That's the distinction.
What About React?
You'll frequently encounter debounce and throttle in React applications.
For example:
function SearchBox() {
const handleSearch = debounce((query) => {
fetchResults(query);
}, 300);
// ...
}
However, there's an important React-specific consideration.
Components can render many times, and functions created during rendering can also be recreated.
So blindly creating a new debounced or throttled function during every render can lead to unexpected behavior.
In real React applications, you need to think about:
- Function identity
- Cleanup
- Dependencies
- Component lifecycle
- Cancelling pending work
Depending on the situation, tools such as useMemo, useCallback, useRef, or custom hooks can help manage this correctly.
The important thing is not to memorize a particular React pattern.
Understand what you're trying to control first.
Don't Use Them Everywhere
Debouncing and throttling are useful, but they aren't performance magic.
Adding them to every event can actually make your code harder to understand.
If an operation is cheap:
button.addEventListener("click", () => {
console.log("Clicked");
});
there's probably no reason to throttle it.
Similarly, if the user expects an immediate response, adding a debounce delay could make the interface feel slower.
The goal isn't:
"Use debounce or throttle whenever an event happens frequently."
The goal is:
Match the frequency of your work to what the application actually needs.
One More Important Difference
There's another way to think about the two.
Debouncing changes when the work happens.
It says:
"Wait until things calm down."
Typing → Typing → Typing → STOP
↓
Execute
Throttling changes how often the work happens.
It says:
"The activity can continue, but I'm only going to process it at this rate."
Scroll → Scroll → Scroll → Scroll → Scroll
↓ ↓
Execute Execute
Once you see it this way, the difference becomes much easier to remember.
The Bigger Engineering Lesson
Debouncing and throttling are really about controlling unnecessary work.
A browser can generate events faster than your application needs to process them.
That doesn't mean the browser is doing something wrong.
It means your application needs to decide:
How often does this work actually need to happen?
For a search box, you probably don't need to search after every keystroke.
For scrolling, you probably don't need to run expensive calculations for every single event.
That's where these techniques become useful.
The 10-Second Cheat Sheet
SEARCH BAR
User types
↓
Wait until they stop
↓
DEBOUNCE
SCROLL
User keeps scrolling
↓
Process periodically
↓
THROTTLE
Or simply remember:
Debounce = "Wait for the pause."
Throttle = "Control the pace."
The real skill isn't knowing the definitions.
It's recognizing when the application doesn't need to react to every single event.
And that's a small optimization that can make a surprisingly big difference in real-world applications.
Top comments (0)