Skip to main content
Practice Problems

useTransition and useDeferredValue in React

Concurrent Features in React 18+

React 18 introduced concurrent rendering with two key hooks: useTransition and useDeferredValue. They allow you to mark certain state updates as non-urgent, keeping the UI responsive.


useTransition

useTransition lets you mark a state update as a transition โ€” a non-urgent update that can be interrupted by more urgent updates (like user input).

tsx
import { useState, useTransition } from "react"; function SearchPage() { const [query, setQuery] = useState(""); const [results, setResults] = useState<string[]>([]); const [isPending, startTransition] = useTransition(); const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { const value = e.target.value; // โœ… Urgent: update input immediately setQuery(value); // โœ… Non-urgent: update results in a transition startTransition(() => { const filtered = heavyFilter(value); // expensive operation setResults(filtered); }); }; return ( <div> <input value={query} onChange={handleChange} /> {isPending && <Spinner />} <ResultList results={results} /> </div> ); }

How useTransition Works

  1. isPending โ€” boolean flag, true while the transition is ongoing
  2. startTransition(callback) โ€” wraps the non-urgent state update
  3. React can interrupt the transition if a more urgent update comes in
  4. The input stays responsive because the heavy update is deferred

useDeferredValue

useDeferredValue creates a deferred copy of a value that "lags behind" the original. React updates it only when it has spare capacity.

tsx
import { useState, useDeferredValue, useMemo } from "react"; function SearchResults({ query }: { query: string }) { // Creates a deferred version of query const deferredQuery = useDeferredValue(query); const isStale = query !== deferredQuery; // Only re-computes when deferredQuery changes const results = useMemo( () => heavySearch(deferredQuery), [deferredQuery] ); return ( <div style={{ opacity: isStale ? 0.5 : 1 }}> {results.map(r => <div key={r.id}>{r.title}</div>)} </div> ); } function App() { const [query, setQuery] = useState(""); return ( <div> <input value={query} onChange={e => setQuery(e.target.value)} /> <SearchResults query={query} /> </div> ); }

useTransition vs useDeferredValue

FeatureuseTransitionuseDeferredValue
What it defersA state update (setState)A value
Where to useWhen you own the state setterWhen you only have the value (props)
Returns[isPending, startTransition]Deferred value
Pending indicatorBuilt-in isPendingCompare original vs deferred

When to Use Which

tsx
// useTransition โ€” you control the setState const [isPending, startTransition] = useTransition(); startTransition(() => { setFilteredList(expensiveFilter(items)); }); // useDeferredValue โ€” you receive the value from parent function ChildComponent({ searchTerm }: { searchTerm: string }) { const deferredTerm = useDeferredValue(searchTerm); // Use deferredTerm for expensive rendering }

Real-World Use Cases

Tab Switching

tsx
function TabContainer() { const [tab, setTab] = useState("home"); const [isPending, startTransition] = useTransition(); function selectTab(nextTab: string) { startTransition(() => { setTab(nextTab); // Heavy tab content can render in background }); } return ( <div> <TabButtons onSelect={selectTab} activeTab={tab} /> {isPending ? <Spinner /> : <TabContent tab={tab} />} </div> ); }

Large List Filtering

tsx
function FilterableList({ items }: { items: Item[] }) { const [filter, setFilter] = useState(""); const deferredFilter = useDeferredValue(filter); const filtered = useMemo( () => items.filter(item => item.name.includes(deferredFilter)), [items, deferredFilter] ); return ( <> <input value={filter} onChange={e => setFilter(e.target.value)} /> <ul style={{ opacity: filter !== deferredFilter ? 0.7 : 1 }}> {filtered.map(item => <li key={item.id}>{item.name}</li>)} </ul> </> ); }

Important:

useTransition and useDeferredValue are React 18+ features for keeping UI responsive during expensive updates. Use useTransition when you control the state setter, and useDeferredValue when you receive a value as a prop. Both are about prioritizing urgent updates (user input) over non-urgent ones (search results, heavy renders).

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems