Mark a State Update as Non-Urgent
useTransition
`useTransition` lets you flag a state update as low-priority. The UI keeps responding to fast user input while the slow update happens in the background.
What you'll learn
- Wrap slow updates in `startTransition`
- Read `isPending` to show a subtle loading hint
useTransition returns [isPending, startTransition]. Calls
inside startTransition are marked low-priority. React keeps the
UI responsive — clicking a different button, typing, or scrolling
gets handled first.
The Shape
function Tabs() {
const [tab, setTab] = useState("home");
const [isPending, startTransition] = useTransition();
function selectTab(next) {
startTransition(() => {
setTab(next); // tagged as non-urgent
});
}
return (
<>
<button onClick={() => selectTab("home")}>Home</button>
<button onClick={() => selectTab("dashboard")}>Dashboard</button>
{isPending && <Spinner />}
<TabPanel tab={tab} />
</>
);
} If <TabPanel tab="dashboard"> renders a heavy chart, the user can
still click other tabs without waiting. isPending is true while
the heavy render is in flight — perfect for a subtle loading hint.
When To Use
- A click triggers an expensive list re-render
- Switching tabs renders something heavy
- A filter or sort produces a slow result
If the update is fast, you don’t need transitions. If it’s slow, transitions keep input snappy at the cost of a slight delay applying the change.
useTransition vs useDeferredValue
| Hook | Where it lives | What it defers |
|---|---|---|
useTransition | Where you trigger the update | A specific setX call |
useDeferredValue | Where the slow render reads a value | A specific value’s identity |
Both achieve the same kind of “urgent vs background” split — pick the one that fits the place you can change.
Caveats
- Transitions can run more than once if React decides to abandon and restart the slow work. The reducer / setter you call has to be safe to run multiple times — pure, no side effects.
- Don’t put fetches inside
startTransition. It only helps with CPU-bound rendering, not network I/O.
Up Next
A specialized hook for subscribing to state that lives outside React — third-party stores, browser APIs.
useSyncExternalStore →