Skip to content

formSheet: ScrollView sizing breaks when nested beyond direct subviews (Fabric) #3634

@marcospgp

Description

@marcospgp

Summary

On Fabric (new architecture), when a ScrollView/FlatList is nested inside intermediate Views within a formSheet screen (e.g., SafeAreaProviderView → wrapper ViewFlatList), the ScrollView overlaps sibling Views (header) and bottom content is clipped by the header's height.

The root cause: childRCTScrollViewComponentAndContentContainer in RNSScreenContentWrapper.mm only searches direct subviews of the content wrapper. Any intermediate Views (SafeAreaProvider, layout boundary Views) hide the ScrollView from the algorithm, so coerceChildScrollViewComponentSizeToSize returns NO and the Fabric (0,0) placement bug goes unfixed.

The Fabric (0,0) bug

The maintainers already identified this in the source (RNSScreenContentWrapper.mm L174-L182):

#ifdef RCT_NEW_ARCH_ENABLED
    // For some unknown yet reason on new architecture the scroll view
    // is placed at (0, 0), which makes no sense given there
    // is another child at lower index in flex layout.
    // TODO: Research why this happens and solve this properly.
    if (newFrame.origin.y == 0) {
      newFrame.origin.y = headerView.frame.size.height;
    }
#endif

This workaround correctly fixes Case 2 (header at index 0, ScrollView at index 1), but only when the ScrollView is a direct subview of RNSScreenContentWrapper.

Reproduction

Layout hierarchy that triggers the bug:

RNSScreenContentWrapper
  └── SafeAreaProvider          ← intermediate native view
      └── View (flex: 1)       ← layout boundary
          ├── Header (opaque)
          └── View (flex: 1)   ← wrapper
              └── FlatList

The algorithm searches self.subviews → finds SafeAreaProvider (not a ScrollView) → returns nil. The frame correction never runs.

Symptoms:

  • First list item hidden behind the header (ScrollView at y=0 instead of below header)
  • Bottom content clipped by exactly the header's height (ScrollView sized to full container instead of container minus header)

Workaround

Adding borderWidth: 0 to the wrapper View around the FlatList fixes both issues:

<View style={{ flex: 1, borderWidth: 0 }}>
  <FlatList style={{ flex: 1 }} ... />
</View>

Empirical testing showed that:

  • collapsable={false} alone: fixes top overlap but NOT bottom clipping
  • backgroundColor alone: fixes neither
  • borderWidth: 0 (even without explicit borderColor): fixes both
  • Removing the wrapper View entirely: fixes bottom but reintroduces top overlap

The borderWidth: 0 appears to affect Yoga's box model calculation — defining border (even as 0) vs leaving it undefined changes how the flex container sizes within formSheet.

Suggested fix

Two possible approaches:

  1. Recursive/deeper ScrollView search: Make childRCTScrollViewComponentAndContentContainer traverse deeper than 1 level to find the ScrollView through intermediate Views
  2. Fix the underlying Fabric (0,0) placement: Resolve the TODO so the workaround algorithm isn't needed at all

Environment

  • react-native-screens: 4.23.0
  • react-native: 0.83.1
  • Fabric (new architecture): enabled
  • Platform: iOS
  • Expo SDK: 53

Metadata

Metadata

Assignees

No one assigned

    Labels

    Missing infoThe user didn't precise the problem enoughMissing reproThis issue need minimum repro scenarioPlatform: iOSThis issue is specific to iOS

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions