Calling createRoot turns the concurrent renderer on, but nothing becomes concurrent by itself. A plain setState is still an urgent update, rendered in one pass and committed as fast as React 7,897 can. Concurrency is opt-in per update, in three ways: wrap a setter in startTransition, read a value through useDeferredValue, or suspend inside a <Suspense> boundary.
| Behavior | Urgent update | Transition update |
|---|---|---|
| Can React pause it? | no | yes, between components |
| Can React discard it? | no | yes, if a newer update arrives |
| Suitable for | input values, toggles, focus | filtering, tab switches, navigation |
Two consequences are easy to miss. A render React abandons has already run your component function, possibly several times, without reaching the DOM — which is why the rules in Pure Rendering matter here: a component that mutates a module variable while rendering does it once per attempt. And data living outside React can change between two slices of one render, leaving half the tree on the old value — tearing, which useSyncExternalStore prevents.