What are "Transition Updates"?
What Transition Updates are
A Transition Update is a low-priority state update that React can run later, pause, or cancel, if more important (urgent) user actions are happening right now.
In simpler terms:
Transitions = updates that can be deferred, so they don't get in the way of a fast UI response.
1. The problem Transitions solve
Before React 18, all calls to setState() were equal:
React ran all updates with the same priority.
Example:
The user types text, and every keystroke filters a huge list:
function Search() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
function handleChange(e) {
const value = e.target.value;
setQuery(value);
setResults(filterBigList(value)); // heavy update
}
return (
<>
<input value={query} onChange={handleChange} />
<ResultsList items={results} />
</>
);
}In React 17 the UI can "lag": the input freezes while React renders the list.
2. How React 18 solves this with Transition Updates
Now you can mark part of the updates as not urgent (transitions):
import { useTransition } from 'react';
function Search() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
function handleChange(e) {
const value = e.target.value;
setQuery(value); // urgent update (typing)
startTransition(() => {
setResults(filterBigList(value)); // deferred (heavy) update
});
}
return (
<>
<input value={query} onChange={handleChange} />
{isPending && <Spinner />} {/* while waiting */}
<ResultsList items={results} />
</>
);
}Now:
- the input field updates instantly;
- React defers filtering the list;
- you can show a loading indicator
isPendingwhile the Transition is not yet finished.
3. What startTransition() does under the hood
- React receives the function passed into
startTransition(); - it marks the updates inside as low priority;
- if urgent updates arrive in the meantime (for example, keystrokes) - React pauses the Transition so the interface doesn't "freeze";
- as soon as the pipeline is free, React resumes the Transition.
4. "Urgent" vs "Transition" updates
| Update type | Example | Priority | Can it be interrupted? | Example API |
|---|---|---|---|---|
| Urgent | Typing, clicks, focus, navigation | High | No | setState() directly |
| Transition (deferred) | Filtering, sorting, loading data | Low | Yes | startTransition() / useTransition() |
React now understands the difference between what's UX-critical and what's resource-heavy.
5. How React decides what to render first
- React works with two update queues:
- urgent (input, focus, scroll, etc.);
- deferred (transitions).
- If the user is typing, urgent updates go through immediately.
- React can:
- defer any transition until the next frame;
- pause it if the user keeps typing;
- cancel it if new data arrives;
- run it again, once the user has stopped.
6. An example with the isPending indicator
useTransition() returns:
isPending- a flag indicating that a Transition is currently running;startTransition(callback)- a function to start low-priority updates.
const [isPending, startTransition] = useTransition();
startTransition(() => {
setSearchResults(fetchFilteredResults(query));
});When React pauses the render, isPending = true -
you can show a spinner, a skeleton, or "Loading…" text.
7. React manages Transitions itself
Transitions only work when Concurrent Rendering is active, that is, when the application's root is created via:
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);If you use the old ReactDOM.render(), startTransition simply has no effect - React cannot pause the render.
8. An example with asynchronous actions
function Products() {
const [filter, setFilter] = useState('all');
const [data, setData] = useState([]);
const [isPending, startTransition] = useTransition();
const handleFilter = (newFilter) => {
startTransition(async () => {
const res = await fetch(`/api/products?filter=${newFilter}`);
const data = await res.json();
setData(data);
setFilter(newFilter);
});
};
return (
<>
<Filters onChange={handleFilter} />
{isPending && <Skeleton />}
<ProductList items={data} />
</>
);
}The UI stays responsive, even if the API request takes time.
9. What's new in React 19
React 19 improved Transitions - now startTransition() can be asynchronous:
await startTransition(async () => {
await someAsyncOperation();
});There is no longer a need to manually manage the pending state - React itself knows when the Transition is done.
Summary
Transition Updates are state updates that React runs smoothly, asynchronously, and without UI lag, deferring them while the user is interacting with the UI.
A quick comparison
| Characteristic | Regular setState | Transition Update |
|---|---|---|
| Priority | High | Low |
| Can be interrupted | No | Yes |
| UI delay | Possible (lag) | None |
| Main use | UI events (typing, clicks) | Long computations, filters, loading |
| API | setState() | useTransition() / startTransition() |
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.