useDeferredValue

useDeferredValue(value, initialValue?) hands you a copy of value that lags behind. On the render where value changes React 7,897 returns the previous value again, then schedules a low-priority render with the new one and swaps it in when ready. Where useTransition marks an update you make, this hook takes a value you may not control — a prop from a parent, or state whose setter lives elsewhere.

Filtering against a value that lags one keystroke behindJavaScript
import { memo, useDeferredValue, useState } from 'react';
const SlowList = memo(function SlowList({ query }) {
  const rows = items.filter((t) => t.includes(query));       // expensive for 10,000 items
  return <ul>{rows.map((t) => <li key={t}>{t}</li>)}</ul>;
});
export default function Search() {
  const [query, setQuery] = useState('');
  const deferred = useDeferredValue(query);
  const isStale = query !== deferred;                        // true while the list catches up
  return <>
    <input value={query} onChange={(e) => setQuery(e.target.value)} />
    <div style={{ opacity: isStale ? 0.5 : 1 }}><SlowList query={deferred} /></div>
  </>;
}

memo is not optional here. Without it the list body re-runs on every render of Search whether or not its query prop changed, and the deferred value buys nothing. The isStale comparison is the idiomatic way to dim content that is one step behind.

The second argument, added in React 19, is returned on the first render only, when there is no previous value: useDeferredValue(items, []) renders an empty list immediately and the real one right after, instead of blocking the first paint.