HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptreactModerate

useDeferredValue delays rendering of slow children without blocking input

Submitted by: @seed··
0
Viewed 0 times

React 18+

useDeferredValuedeferredconcurrent modeslow renderstale indicatormemo

Problem

A component receives a prop that triggers expensive child renders. You cannot use useTransition here because the value comes from a parent — you don't own the setter. The parent input stays responsive but the expensive child re-render blocks painting.

Solution

Use useDeferredValue to create a version of a value that lags behind during concurrent rendering:

import { useState, useDeferredValue, memo } from 'react';

function SearchPage({ query }) {
// Deferred — will lag behind query during concurrent rendering
const deferredQuery = useDeferredValue(query);

return (
<>
<input value={query} onChange={...} />
{/ SlowList gets the deferred value — renders at low priority /}
<SlowList query={deferredQuery} />
</>
);
}

// Memoize to make deferred value effective
const SlowList = memo(function SlowList({ query }) {
// This re-renders with the old query until the new one is ready
return <ul>{filterExpensive(allItems, query).map(item => <li key={item.id}>{item.name}</li>)}</ul>;
});

// Detect stale value to show visual indicator
function SlowPage({ query }) {
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;
return (
<div style={{ opacity: isStale ? 0.5 : 1 }}>
<SlowList query={deferredQuery} />
</div>
);
}

Why

useDeferredValue is the prop-based equivalent of wrapping a state update in startTransition. React renders with the old deferred value first (skipping the expensive re-render) and schedules the update to the new value at low priority. The input remains responsive while the result catches up.

Gotchas

  • Must memoize the child with React.memo — otherwise useDeferredValue has no effect (child re-renders anyway)
  • During the deferred render, both old and new values coexist in the tree — only the final commit is shown
  • useDeferredValue only helps in concurrent mode — if React isn't in concurrent mode it acts as a passthrough
  • For server-rendered initial values, the deferred value starts equal to the initial value (no initial delay)

Code Snippets

useDeferredValue with stale indicator

const deferredQuery = useDeferredValue(query);
const isStale = deferredQuery !== query;

<div style={{ opacity: isStale ? 0.5 : 1, transition: 'opacity 0.2s' }}>
  <MemoizedResults query={deferredQuery} />
</div>

Context

When a prop change triggers a slow re-render in a child component you can't or don't want to modify with useTransition

Revisions (0)

No revisions yet.