useDeferredValue returns either the current value or its previous value, allowing urgent UI to update while rendering that depends on a slower value catches up. It works with Concurrent Mode and is useful when new data would otherwise replace usable content with a spinner. section 1.4.1 section 13.1.4
Use the hook by tracking a value and separating immediate rendering from potentially delayed rendering:
- Call const deferredValue = useDeferredValue(value). The hook may return the new value when rendering succeeds immediately or the old value while React waits. section 13.1.4
- Pass the current value to components that should respond immediately, and the deferred value to components that may suspend or take longer to render. section 13.1.4
- Detect the temporary mismatch with const isPending = deferredValue !== value, then show a spinner or reduce opacity to signal stale content. section 13.1.4
Keeping user details visible during selection
When a Users page switches from Mark to Clarisse, UsersList receives the latest user immediately and can show inline progress. UserDetails receives deferredUser, so it continues showing Mark until Clarisse’s details finish rendering. Initialize defensively with const deferredUser = useDeferredValue(user) || user. section 13.1.4
Listing 13.6 Passing a deferred value to UserDetails
import {
useState,
unstable_useDeferredValue as useDeferredValue,
Suspense
} from "react";
// other imports
export default function UsersPage () {
const [loggedInUser] = useUser();
const [selectedUser, setSelectedUser] = useState(null);
const user = selectedUser || loggedInUser;
const queryClient = useQueryClient();
const deferredUser = useDeferredValue(user) || user; #1
const isPending = deferredUser !== user; #2
function switchUser(nextUser) {
setSelectedUser(nextUser); #3
queryClient.prefetchQuery(/* prefetch user details */);
queryClient.prefetchQuery(/* prefetch user image */);
}
return user ? (
<main className="users-page">
<UsersList
user={user} #4
setUser={switchUser}
isPending={isPending} #5
/>
<Suspense fallback={<PageSpinner/>}>
<UserDetails
userID={deferredUser.id} #6
isPending={isPending} #7
/>
</Suspense>
</main>
) : <PageSpinner/>;
}
#1 Track the user value: return the old if the new delays rendering.
#2 Create a pending flag that’s true if the deferred value is stale.
#3 Update the user value.
#4 Let the list know the new user.
#5 Let the list know its user is inconsistent with UserDetails.
#6 Show the old user details while waiting for the new.
#7 Let UserDetails know its user is stale.
Inspect the complete UsersPage wiring for immediate and deferred props, pending-state detection, and query prefetching. section 13.1.4

