useDeferredValue — Keep Input Snappy During Slow Renders
Defer updating slow results so typing stays responsive.
Introduction
useDeferredValue lets React treat some updates as lower priority. You can keep user input instant while deferring slow, derived renders—like big filtered lists—until the browser has time.
Why this matters
Some renders are slow—big lists, heavy formatting. useDeferredValue lets React prioritize urgent updates (like typing) and defer lower‑priority work (like rendering the slow list).
The problem
Passing the raw query to an expensive component forces it to re-render on every keypress.
Inefficient approach
UI stutters because the slow list renders on each keystroke:
import { useState } from "react";
function SlowList({ query }) {
const items = Array.from({ length: 10000 }, (_, i) => `Item ${i + 1}`);
const filtered = items.filter(item => item.toLowerCase().includes(query.toLowerCase()));
return (
<ul>
{filtered.slice(0, 100).map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
function App() {
const [query, setQuery] = useState("");
return (
<div>
<h1>useDeferredValue</h1>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
<SlowList query={query} />
</div>
);
}
export default App;
The solution
Defer the query value passed to an expensive component.
import { useState, useDeferredValue } from "react";
function SlowList({ query }) {
const items = Array.from({ length: 10000 }, (_, i) => `Item ${i + 1}`);
const filtered = items.filter(item => item.toLowerCase().includes(query.toLowerCase()));
return (
<ul>
{filtered.slice(0, 100).map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
function App() {
const [query, setQuery] = useState("");
const deferredQuery = useDeferredValue(query);
return (
<div>
<h1>useDeferredValue</h1>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
<SlowList query={deferredQuery} />
</div>
);
}
export default App;
Step-by-step
-
Keep the controlled input bound to
query. -
Create
deferredQuery = useDeferredValue(query). -
Pass
deferredQuerydown to the slow component. -
Memoize the slow component where appropriate to avoid unnecessary work.
Tips
-
Combine with
memofor child components that render based on the deferred value. -
Don’t defer critical UI (like validation errors that must be immediate).
-
Measure before and after—defer only what’s truly slow.
Knowledge base
Problem snippet:
<SlowList query={query} />
Solution snippet:
const deferredQuery = useDeferredValue(query);
<SlowList query={deferredQuery} />