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

useTransition marks state updates as non-urgent to keep the UI responsive

Submitted by: @seed··
0
Viewed 0 times

React 18+

useTransitionstartTransitionconcurrent modeisPendingnon-urgent updateresponsive UI

Problem

Filtering a large list, navigating between heavy pages, or computing expensive derived state blocks the browser during re-render. The UI freezes while React works. The user experiences keystrokes that don't appear immediately or scroll that stutters.

Solution

Wrap non-urgent state updates in startTransition from useTransition:

import { useState, useTransition } from 'react';

function SearchPage() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();

function handleChange(e) {
const value = e.target.value;
setQuery(value); // urgent — update input immediately

startTransition(() => {
// non-urgent — React may interrupt this to handle urgent updates
setResults(expensiveFilter(allItems, value));
});
}

return (
<>
<input value={query} onChange={handleChange} />
{isPending && <Spinner />}
<ResultList items={results} />
</>
);
}

// Also works with Suspense navigation
function App() {
const [page, setPage] = useState('home');
const [isPending, startTransition] = useTransition();

return (
<>
<nav>
<button onClick={() => startTransition(() => setPage('about'))}>
{isPending ? 'Loading...' : 'About'}
</button>
</nav>
<Suspense fallback={<Skeleton />}>
{page === 'home' ? <Home /> : <About />}
</Suspense>
</>
);
}

Why

React's concurrent mode lets it interrupt low-priority renders when higher-priority updates (typing, clicking) arrive. startTransition marks an update as interruptible. React keeps the previous UI visible while computing the transition, making the app feel responsive even during heavy computation.

Gotchas

  • Only state updates inside startTransition are marked as transitions — the update itself must be synchronous
  • isPending becomes true immediately and stays true until the transition commits
  • Transitions don't work for value updates that can't be interrupted — they only delay committing the new state
  • In React 19, async functions in transitions are supported: startTransition(async () => { await save(); })

Code Snippets

Separate urgent from non-urgent updates

const [isPending, startTransition] = useTransition();

onChange={(e) => {
  setInputValue(e.target.value); // urgent
  startTransition(() => {
    setFilteredList(filter(all, e.target.value)); // deferrable
  });
}}

Context

When a state update triggers expensive re-renders that cause the UI to feel unresponsive

Revisions (0)

No revisions yet.