Skip to content
Rendering & Virtual DOMmedium

Transitions (startTransition)

startTransition marks a state update as non-urgent, telling React it can be interrupted by higher-priority updates like user input. The UI stays responsive even during expensive re-renders.

Memory anchor

startTransition is a 'low priority' stamp on a mail package — the post office (React) delivers express mail (user input) first and comes back to your package when there's time, discarding it if you send a replacement.

Expected depth

Usage: startTransition(() => setSearchResults(filtered)). While the transition is pending, isPending from useTransition is true, useful for showing a spinner. Transitions don't delay the update — they allow React to interrupt it. If the user types again before the transition completes, React discards the in-progress render and starts a new one with the latest value. useDeferredValue is the value-based equivalent: it returns a deferred version of a value that lags behind during transitions.

Deep — senior internals

Under the hood, startTransition marks the update with a TransitionLane — a lower-priority lane than SyncLane (user input). The scheduler can interrupt transition rendering when a sync update arrives. This is the core benefit of concurrent rendering: without it, React would block the main thread completing the transition render, making the UI unresponsive. Transitions compose with Suspense: a transition can trigger a Suspense boundary without immediately showing the fallback (it shows the old UI as stale instead). This prevents loading state flicker for fast transitions.

🎤Interview-ready answer

startTransition marks state updates as interruptible low-priority work. The UI stays responsive because React handles user input at a higher priority, discarding in-progress transition renders if new input arrives. I use useTransition for the isPending flag to show loading indicators, and useDeferredValue for derived values. Transitions compose with Suspense to avoid flashing loading states.

⚠Common trap

Wrapping synchronous computations in startTransition and expecting them to be 'debounced'. Transitions don't defer execution — they mark the update as interruptible. The computation still runs; it can just be interrupted.