A useDebounce Hook returns a value that updates only after the source value stops changing for a set delay. It is the standard way to prevent a search box from firing a request on every keystroke. You keep the immediate value for the input and the delayed value for the expensive work.
Delay with a timer
Debouncing waits for quiet. When the value changes, start a timeout.
If the value changes again before the timeout finishes, cancel the old timer and start a new one. The debounced value updates only after the delay passes without another change.
Build the Hook
import { useEffect, useState } from "react";
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}The Effect runs each time value or delay changes. Its cleanup clears the pending timer, so only the most recent value survives to update the state.
Skipping the cleanup would let an older timer still fire and overwrite the newer value. useState initializes the debounced value to the first source value, so there is no empty window on the initial render.
Use it for a search input
function SearchBox() {
const [query, setQuery] = useState("");
const debouncedQuery = useDebounce(query, 400);
return (
<div>
<input value={query} onChange={(event) => setQuery(event.target.value)} />
<p>Searching for: {debouncedQuery}</p>
</div>
);
}The input shows query instantly, but debouncedQuery catches up only after the user pauses for 400 milliseconds. In a real app, you would start the request from debouncedQuery instead of displaying it, so the network only fires once the user stops typing.
Debounce vs useDeferredValue
Debounce adds a real timer delay, which is what you want when the goal is fewer requests or fewer expensive actions. useDeferredValue defers a value to keep rendering responsive and has no fixed delay, but it does not prevent extra network requests by itself.
| Concern | useDebounce timer | useDeferredValue |
|---|---|---|
| What it controls | When a value updates | When rendering happens |
| Delay | Fixed, chosen by you | None, adapts to device |
| Reduces network requests | Yes | No |
Use a timer when you want to wait for the user to stop typing. Use useDeferredValue when a slow UI part should lag behind without blocking input. The full comparison covers the decision.
When to reach for debounce
- Search and autocomplete requests while the user types.
- Saving a form after the user pauses.
- Any expensive action that should not run on every change.
If the user pauses for the full delay, the debounced value updates once, and only then does the work run. The useDebounce Hook is not needed for values that only drive rendering. See how to create a custom Hook for the extraction steps.
Rune AI
Key Insights
- useDebounce returns a value that updates after the source stops changing.
- Use setTimeout inside an Effect and clearTimeout in cleanup.
- Keep the immediate value for the input and the debounced value for the request.
- Prefer useDeferredValue when deferring rendering, not requests.
- Choose a delay around 300 to 500 milliseconds for search.
Frequently Asked Questions
Why does the useDebounce Hook need cleanup?
What is the difference between useDebounce and useDeferredValue?
What delay should I choose for a search input?
Conclusion
A useDebounce Hook keeps an immediate value and a delayed copy in sync through a timer. Use it to wait for the user to stop typing before firing a search or saving, and prefer useDeferredValue when the goal is rendering, not rate limiting.
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.