-
Notifications
You must be signed in to change notification settings - Fork 49.7k
Description
React version:
18.2.0 (reproduced in latest 19.1 as well)
Summary
There is an open issue proposing documentation updates around the use of flushSync with createRoot().render() to enforce synchronous rendering. However, since that issue has gone without feedback for several months, and given the core behavior observed, I suspect this may be an actual bug in React rather than just a documentation gap. I'm opening this issue here in the main React repository to clarify whether this behavior is intentional or not.
Steps To Reproduce
- Render a basic React app using
createRoot(...).render(<App />). - Inside
<App />, log messages fromuseLayoutEffectanduseEffect. - Log messages before and after
root.render()call. - Compare behavior between:
createRoot(...).render(...)(modern API)flushSync(() => root.render(...))- Legacy
ReactDOM.render(...)
Link to code example: CodeSandbox
The current behavior
Using createRoot(...).render(...) without wrapping in flushSync, we observe that:
- The log inside
useLayoutEffectis emitted afterconsole.log("Post root.render"), indicating the effect happens after the render call resolves. - In contrast, using
flushSync(() => root.render(...))or legacyReactDOM.render(...),useLayoutEffectis called before the post-render log.
This suggests the modern root API allows post-render behavior to interleave with layout effects, even outside of explicitly opted-in concurrent features. This might violate expectations around synchronous behavior of the initial render.
The expected behavior
As @rickhanlonii noted in a related discussion, the initial render should be synchronous by default. If that’s the case, this behavior seems inconsistent.
createRoot(...).render(...)should preserve layout effect timing consistency with legacyReactDOM.render(...), without needing to wrap influshSync.- If the current behavior is intentional, it would help to explicitly document that
createRoot(...).render(...)may be async under certain conditions—even without concurrent features being enabled.