Repository navigation
[iOS][Fabric] ScrollView content permanently invisible: inner RCTEnhancedScrollView left at CGRectZero when the autoresizing delta is missed #57520
Description
Description
On the New Architecture, RCTScrollViewComponentView sizes its inner RCTEnhancedScrollView only via an autoresizing mask: the scroll view is created with initWithFrame:self.bounds — which is CGRectZero at component-view creation — and relies on UIViewAutoresizingFlexibleWidth | FlexibleHeight deltas to grow when the shell's frame is later applied (source).
Autoresizing only propagates deltas at the moment the parent's frame changes. Under heavy mount/teardown churn (reliably reproducible on a thermally-throttled device, where JS-driven mount choreography stretches from milliseconds to seconds), that delta moment can be missed. The inner UIScrollView is then left at CGRectZero permanently — there is no code path that ever re-establishes the invariant:
- the shell has its correct Yoga frame (
onLayoutfires normally), onContentSizeChangefires normally (state events come from the ShadowTree, not the native view),- the content subtree is fully mounted and correctly laid out inside the scroll view,
- but everything is clipped into a zero-size viewport → content is invisible while the app believes everything is fine.
Captured native hierarchy of a live occurrence (Xcode view debugger, real device):
RCTScrollViewComponentView frame = (0, 429, 430, 503) ← correct Yoga frame
RCTEnhancedScrollView frame = (0, 0, 0, 0) ← never sized
UIView (container) frame = (0, 0, 430, 1024) ← matches contentSize
… full content tree, all frames correct, invisible …
To identify the writer we swizzled -[RCTEnhancedScrollView setFrame:] to veto+stack-trace any zero-size write: it never fires. Nothing writes a zero frame — the view is born at zero and the grow-delta is missed. This distinguishes the bug from external interference and points at the reliance on autoresizing itself.
Frequency in the wild: on a hot iPhone 15 Pro Max (thermal state "serious") this hit nearly every mount of screens whose ScrollView mounts one frame after the screen (skeleton→content swap), across multiple independent screens. It is also consistent with long-standing "ScrollView renders invisible after logout/onboarding transition" reports that apps (including ours) have historically dodged with full app reloads.
Related (but distinct, both recycling-related — recycling is off by default and was disabled in our repro): #55090, #34165.
Steps to reproduce
The failure is a race, so there is no minimal synchronous repro; the deterministic statement of the bug is:
RCTScrollViewComponentViewis created (frameCGRectZero) →_scrollViewcreated at zero with autoresizing W+H.- If the shell's frame assignment lands in any state where autoresizing cannot propagate the delta to
_scrollView, the scroll view stays 0×0. - No subsequent code (
updateLayoutMetrics, state updates,layoutSubviews) ever corrects it → permanent invisible content.
Practical reproduction (how we hit it ~every mount): iPhone at thermal state serious (charge + load), a screen that mounts its ScrollView one requestAnimationFrame after the screen mounts, navigate to the screen repeatedly. Verified on RN 0.83.6 / iOS 26.5 / New Architecture / Hermes; the relevant code is unchanged on main.
Expected / actual
- Expected: the inner scroll view always tracks the shell's bounds.
- Actual: it can be left at
CGRectZeroforever; all content invisible; no error anywhere (JS, ShadowTree, or UIKit).
Fix
PR attached: enforce the invariant explicitly — re-glue _scrollView to the shell's bounds in updateLayoutMetrics and layoutSubviews (bounds.size + center rather than frame, so the RTL transform is respected and bounds.origin = contentOffset is preserved). Verified on-device: the desync still occurs under the same conditions and now heals within one layout pass; the invisible-content states no longer reproduce.
Activity
react-native-bot commentedon Jul 11, 2026
Warning
Missing reproducer: We could not detect a reproducible example in your issue report. Reproducers are mandatory and we can accept only one of those as a valid reproducer:
You can read more about about it on our website: How to report a bug.
react-native-bot commentedon Aug 4, 2026
This issue is waiting for author's feedback since 24 days. Please provide the requested feedback or this will be closed in 7 days.
react-native-bot commentedon Aug 29, 2026
This issue is waiting for author's feedback since 24 days. Please provide the requested feedback or this will be closed in 7 days.
react-native-bot commentedon Sep 5, 2026
This issue was closed because the author hasn't provided the requested feedback after 7 days.