useDeferredValue Hook
useDeferredValue Hook — deferring values for UI optimization
useDeferredValue is a React 18 hook that allows React to defer value updates, keeping the UI fast and responsive. It's similar to the debounce concept, but at React's level — without additional timeouts.
When you have an input and a large list that depends on that input, React can maintain the input's immediate response and slightly delay the list update — preventing UI slowdown.
const deferredValue = useDeferredValue(value);
If `value` changes, React will update it immediately for the UI, but `deferredValue` will only update when React is free from heavy rendering.
import React, { useState, useDeferredValue, useMemo } from 'react';
export default function SearchDeferred() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
const items = useMemo(() => {
const data = Array.from({ length: 5000 }, (_, i) => `Item ${i + 1}`);
return data.filter((item) => item.toLowerCase().includes(deferredQuery.toLowerCase()));
}, [deferredQuery]);
return (
<div>
<h2>🔍 useDeferredValue Filtering</h2>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Write something..."
/>
<p>
{query !== deferredQuery ? '⏳ Updating...' : '✅ Ready'}
</p>
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
In this example, the user can type in the input without delay, because React defers the heavy filtering process until they finish typing. `deferredQuery` updates slightly later, but the UI always remains fast.
| useTransition | useDeferredValue |
|---|---|
| Manages the update (action) | Manages the value (data) |
| Used to mark an update as non-urgent | Used to defer an existing state value |
| Requires startTransition(callback) | Simply pass value — get deferred version |
| Suitable for managing state changes | Suitable for derived data calculation |
// useTransition version
const [isPending, startTransition] = useTransition();
startTransition(() => {
setFiltered(items.filter(...));
});
// useDeferredValue version
const deferredQuery = useDeferredValue(query);
const filtered = useMemo(() =>
items.filter(item => item.includes(deferredQuery)), [deferredQuery]);
Both solve the UI lag problem, but useDeferredValue is simpler when working with one specific value.
import React, { useState, useDeferredValue } from 'react';
import { LineChart, Line, XAxis, YAxis } from 'recharts';
export default function DeferredChart() {
const [range, setRange] = useState(100);
const deferredRange = useDeferredValue(range);
const data = Array.from({ length: deferredRange }, (_, i) => ({
x: i,
y: Math.sin(i / 10) * 50 + 50,
}));
return (
<div>
<h3>📊 Deferred Chart Example</h3>
<input
type="range"
min="100"
max="5000"
value={range}
onChange={(e) => setRange(Number(e.target.value))}
/>
<p>Elements: {deferredRange}</p>
<LineChart width={500} height={200} data={data}>
<XAxis dataKey="x" />
<YAxis />
<Line type="monotone" dataKey="y" stroke="#2196f3" />
</LineChart>
</div>
);
}
Here React doesn't try to immediately rerender on every slider movement. Instead, it waits until the deferred value updates, maintaining the chart's smooth operation.
Write a component that:
Try comparing the same code without useDeferredValue. You'll see the input responds slowly with large data.