CodePark/ questions / React rendering performance
START PRACTICINGPRACTICE
Frontend question · Browser & web performance › React rendering performance

A search box filters a slow list on every keystroke, and typing feels laggy. Compare debouncing the input with useDeferredValue. When would you use each?

function Search({ items }) {
  const [query, setQuery] = useState("");
  const deferredQuery = useDeferredValue(query);
  return (
    <>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      <SlowList items={items} query={deferredQuery} />
    </>
  );
}
const SlowList = memo(function SlowList({ items, query }) { /* ... */ });

Both keep the input fast, but in different ways.

Debouncing waits until the user stops typing for a fixed time, for example 300 ms, and only then updates the list. It does less work in total. But the list always lags by at least 300 ms, even on a fast phone. Use it when each update is costly outside React too, like a network request per search.

useDeferredValue lets React update the input first, at high priority. Then React re-renders the list with the new value in the background, at low priority. If the user types again, React throws away that unfinished render and starts with the newer value. So:

  • On a fast device, the list updates almost at once.
  • On a slow device, the list falls behind, but typing never freezes.
  • There is no fixed delay to tune.

In the code, SlowList must be wrapped in memo. During the urgent render, deferredQuery still has the old value. memo lets React skip SlowList in that render. Without memo, the slow list renders in the urgent update too, and nothing improves.

In short: use useDeferredValue (or useTransition) for slow rendering. Use debouncing for slow or costly side effects like fetches. You can use both: debounce the fetch and defer the render.

  • Debouncing waits for a pause in typing, so the list always lags by the delay.
  • Debouncing is best for costly side effects like a fetch per keystroke.
  • useDeferredValue renders the input first and the list later, at low priority.
  • React throws away an unfinished deferred render when a newer value arrives.
  • The slow component must be wrapped in memo, or deferring does not help.