DevAcademy
LearnReactuseDeferredValue
IntermediateReact

useDeferredValue

Learn how to defer re-rendering a non-urgent part of the UI until more urgent updates have finished.

Reading Time

14 min

Lesson

Lesson 23 of 42

What useDeferredValue Does

useDeferredValue(value) returns a version of value that "lags behind" during urgent updates, catching up once the browser has spare capacity. It’s useful when a fast-changing value (like text being typed) feeds into an expensive part of the UI you don’t want to slow down typing itself.

Deferring an Expensive List

import { useState, useDeferredValue } from "react";

function SearchPage({ allItems }) {
  const [query, setQuery] = useState("");
  const deferredQuery = useDeferredValue(query);

  return (
    <>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      {/* This expensive list re-renders using the deferred, "lagging" value */}
      <ExpensiveResultsList query={deferredQuery} items={allItems} />
    </>
  );
}

The Input Stays Responsive

Because query updates immediately (feeding the input’s own value), typing always feels instant. deferredQuery only catches up once React has time to re-render the expensive list — so a slow list render never blocks the input from updating on screen.

Showing Stale Content is Visible, Deliberately

While the deferred value is "catching up," the UI briefly shows results based on the previous query — you can detect this with a simple comparison and dim the outdated content to signal that it’s about to update.

Indicating Stale Content

function SearchPage({ allItems }) {
  const [query, setQuery] = useState("");
  const deferredQuery = useDeferredValue(query);
  const isStale = query !== deferredQuery;

  return (
    <>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      <div style={{ opacity: isStale ? 0.6 : 1 }}>
        <ExpensiveResultsList query={deferredQuery} items={allItems} />
      </div>
    </>
  );
}

useTransition vs useDeferredValue

useTransitionuseDeferredValue
What it wrapsA state update you triggerA value you’ve already received (often a prop)
ControlYou call startTransition yourselfYou just pass in a value — no event handler needed
Typical useAn onChange handler that also updates expensive stateA slow child component fed by a fast-changing prop

Not the Same as Debouncing

A debounce waits a fixed delay before updating, regardless of how busy the browser is. useDeferredValue instead adapts to the actual device and workload — it catches up as soon as there’s spare rendering capacity, which can be faster or slower than any fixed delay.

Best Practice

Reach for useDeferredValue when an expensive part of the UI is fed by a value you don’t directly control the update of (like a prop), and useTransition when you do control the triggering event yourself.

Interview Questions

Quick Quiz

1. What does useDeferredValue return?

2. How does useDeferredValue differ from a fixed-delay debounce?

3. When is useDeferredValue generally preferred over useTransition?