Debouncing vs Throttling: When to Use Which
When I first heard about debouncing and throttling, I thought they were just fancy terms that senior developers used to sound smart. But after dealing with laggy search bars and jumpy scroll animations in my projects, I realized these are actually lifesavers that every developer should know.
If you've ever wondered why your search function is making too many API calls or why your scroll event is making the page slow, this article is for you.
What's the Problem?
Let me start with a story. Last month, I was building a search feature where users could type and see live results. Simple, right? Here’s what I wrote first:
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', (e) => {
searchAPI(e.target.value);
});
Looks fine, but there's a huge problem. Every single keystroke was making an API call. If someone typed 'javascript' that's 10 API calls! The server was getting hammered, and the user experience was terrible.
This is where debouncing and throttling come to rescue us
Debouncing: Wait Until They Stop
Think of debouncing like waiting for someone to finish talking before you respond. You don't interrupt them after every word, you wait for a pause
Debouncing delays the execution until after a certain time has passed since the last call.
fixed my search problem:
function debounce(func, delay) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
const searchInput = document.getElementById('search');
const debouncedSearch = debounce((query) => {
searchAPI(query);
}, 500);
searchInput.addEventListener('input', (e) => {
debouncedSearch(e.target.value);
});
Now, the API only gets called 500ms after the user stops typing. Much better!
When to Use Debouncing:
- Search bars — wait until typing stops
- Form validation — don’t validate every keystroke
- Save buttons — prevent multiple clicks
- Window resize events — wait until resizing ends
Throttling: Control the Rate
Throttling is different. It's like having a rule that you can only speak once every minute, no matter how much you want to say.
Throttling limits how often a function can be called within a specific time period
example with scroll events:
function throttle(func, limit) {
let inThrottle;
return function (...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
const handleScroll = throttle(() => {
console.log('User is scrolling');
// Update navbar, check scroll position, etc.
}, 100);
window.addEventListener('scroll', handleScroll);
This ensures the scroll handler only runs once every 100ms, instead of firing hundreds of times per second When to Use Throttling:
- Scroll events — Update UI elements smoothly
- Mouse movement tracking — For games or interactive elements
- API rate limiting — Ensure you don't exceed limits
- Button clicks — Prevent spam clicking
The Key Difference (In Simple Terms)
Debouncing: “I'll do it after you stop bothering me for X seconds”
Throttling: “I'll do it, but only once every X seconds, no matter what”
Here's a visual way to think about it:
User types “hello” in 1 second:
- Without any optimization: 5 API calls (h, e, l, l, o)
- With debouncing (500ms): 1 API call (after they stop typing)
- With throttling (200ms): 2–3 API calls (at regular intervals while typing)
Real Examples in React
Since most of us work with React, here are some practical hooks:
Custom Debounce Hook:
import { useState, useEffect } from 'react';
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
// Usage in component
function SearchComponent() {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearchTerm = useDebounce(searchTerm, 500);
useEffect(() => {
if (debouncedSearchTerm) {
// Make API call here
searchAPI(debouncedSearchTerm);
}
}, [debouncedSearchTerm]);
return (
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
);
}
Custom Throttle Hook:
import { useRef, useCallback } from 'react';
function useThrottle(callback, delay) {
const lastRan = useRef(Date.now());
return useCallback((...args) => {
if (Date.now() - lastRan.current >= delay) {
callback(...args);
lastRan.current = Date.now();
}
}, [callback, delay]);
}
// Usage
function ScrollComponent() {
const throttledScrollHandler = useThrottle(() => {
console.log('Scroll event handled');
}, 100);
return (
<div onScroll={throttledScrollHandler}>
{/* Your scrollable content */}
</div>
);
}
Common Mistakes I Made (So You Don't Have To)
- Using debouncing for everything — I thought debouncing was always better. Wrong! For things like scroll animations, throttling gives smoother results.
- Wrong delay times — I used 1000ms debounce for search. Users thought the app was broken! 300–500ms is usually sweet spot.
- Not cleaning up — In React, always clean up your timeouts in useEffect cleanup functions.
- Over-engineering — Don't use these techniques where they're not needed. A simple button click doesn't need debouncing unless it's making API calls.
Quick Decision Guide
Ask yourself:
- Do I want to wait until activity stops? => Use debouncing
- Do I want to limit how often something happens? => Use throttling
- Is it a search/input field? => Probably debouncing
- Is it scroll/mouse events? > Probably throttling
Libraries vs Custom Implementation
While I showed custom implementations above, libraries like Lodash have well-tested versions:
import { debounce, throttle } from 'lodash';
const debouncedSearch = debounce(searchFunction, 500);
const throttledScroll = throttle(scrollHandler, 100);
For production apps, I usually go with Lodash because it handles edge cases I might miss. Conclusion
Debouncing and throttling aren't just optimization techniques, they're essential tools for building smooth user experiences. They help you be kind to your servers, improve performance, and make your apps feel more responsive.
Start small: add debouncing to your next search feature or throttling to your scroll handlers. You'll immediately see the difference, and your users (and servers) will thank you.
Remember: debouncing waits for calm, throttling controls the storm. Both have their place in a developer's toolkit.
Hit me up on X with the links below if you want to discuss pretty much anything.
Thank you for reading!