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
| useTransition | useDeferredValue | |
|---|---|---|
| What it wraps | A state update you trigger | A value you’ve already received (often a prop) |
| Control | You call startTransition yourself | You just pass in a value — no event handler needed |
| Typical use | An onChange handler that also updates expensive state | A 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.