useDeferredValue defers a re-render inside React and stays interruptible, while debouncing delays any kind of work by a fixed timer outside React. The difference is where the work runs and whether the user's next keystroke can interrupt it.
Both solve the same visible problem, a slow input, but they operate on different parts of the pipeline. Debouncing sits outside React, and useDeferredValue works inside the render.
The difference at a glance
| Aspect | Debouncing | useDeferredValue |
|---|---|---|
| Timing | Fixed delay you choose | No fixed delay, adapts to the device |
| Interruptible | No, work blocks when it runs | Yes, background renders are interruptible |
| Scope | Any work, including network calls | Render work only |
| Suspense | Not integrated | Keeps the previous value while content loads |
How debouncing works
Debouncing waits until the user stops typing before running the work, usually with setTimeout and a custom hook. The hook returns a value that updates only after the delay, which callers then pass down to the slow work.
import { useEffect, useState } from "react";
function useDebounce(value, delay) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const id = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(id);
}, [value, delay]);
return debounced;
}The cleanup clears the previous timer on every change, so the value only updates after the user pauses. This is great for cutting down network requests, but the delayed work still blocks the main thread when it finally runs.
With a 300ms delay, nothing updates while the user types, then the result appears once they pause. That is why debounced searches feel calm but always lag a beat behind the input.
How useDeferredValue works
useDeferredValue keeps a value lagging behind the latest one and re-renders the slow part in the background.
import { useState, useDeferredValue } from "react";
export default function Search() {
const [query, setQuery] = useState("");
const deferredQuery = useDeferredValue(query);
return (
<>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<SearchResults query={deferredQuery} />
</>
);
}The input updates immediately with the new query. SearchResults keeps the previous deferredQuery and re-renders with the new value in the background. If another keystroke arrives, React abandons that background render and starts again, so typing never blocks.
The list visibly lags behind the input instead of freezing it. You can dim the stale results until the new ones arrive, which gives the user a clear signal that the list is catching up.
For a slow child to actually skip work, wrap it in memo so it can reuse its previous render while deferredQuery is unchanged. This only defers rendering; it does not reduce the number of requests.
Why the built-in Hook adapts better
useDeferredValue needs no fixed delay. On a fast machine the background render finishes almost instantly, and on a slow phone it lags proportionally. Debouncing always waits the same amount of time and then blocks, which still causes jank when the work finally runs.
Choosing a debounce delay is also a guess: too short and the list still lags, too long and the app feels unresponsive. The Hook removes that guess by tying the deferral to how long the render actually takes.
The Hook is also integrated with Suspense. If the background render suspends, the user keeps seeing the previous results instead of a fallback. That is the same machinery useTransition uses, and it pairs with code splitting boundaries.
Which should you use?
- Use useDeferredValue when a slow render is blocking input.
- Use debouncing when you want fewer network requests.
- Use both together: debounce the request and defer the render.
Pick the tool that matches the work. If the problem is rendering, reach for the Hook. If the problem is a flood of requests, debounce first.
Either way, measure before optimizing so you change the part that is actually slow.
A live search is the clearest example. Debounce the request so each keystroke does not hit the server, then defer the result list's render so typing stays responsive while results arrive.
Rune AI
Key Insights
- useDeferredValue defers a re-render and stays interruptible.
- Debouncing waits a fixed delay before running work.
- The Hook needs no fixed delay and adapts to the device.
- Debouncing is still the right tool for fewer network requests.
- Use the Hook for render work, debouncing for requests.
Frequently Asked Questions
Does useDeferredValue reduce network requests?
Do I need memo for useDeferredValue to help?
Conclusion
useDeferredValue defers rendering inside React and stays interruptible, while debouncing delays any kind of work with a fixed timer. Use the Hook for slow renders that block typing, and keep debouncing for the network side of the same feature.
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.