You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
SectionList with sticky section headers jumps back and forth #58925
In a long SectionList with stickySectionHeadersEnabled, the list can break once you've scrolled down a bit. It starts jumping back and forth, flipping between two sets of rows.
Claude found what appears to be a fix by patching ScrollViewStickyHeader. This can be flipped on in the reproducer app.
The following is Claude's analysis. I have not verified if any of this is true:
ScrollViewStickyHeader forwards its layout to child.props.onLayout, and clones the child with onLayout: undefined. But the children VirtualizedList passes are CellRenderer elements, which seem to take onCellLayout rather than onLayout. If so, the cell keeps its own onLayout and measures itself relative to the sticky wrapper (y = 0), and ListMetricsAggregator.notifyCellLayout stores that as the header's offset. The comment in VirtualizedListCellRenderer ("when this is a sticky header, onLayout will get automatically extracted and called explicitly by ScrollViewStickyHeader") suggests the forwarding was meant to happen.
If section headers are recorded at offset 0, that could explain the jumping. VirtualizedList sizes each spacer as lastMetrics.offset + lastMetrics.length - firstMetrics.offset. Once the render window leaves the always-rendered initialNumToRender region, a spacer starts at cell initialNumToRender. When that cell is a section header, the spacer would come out too tall by the header's real offset. The rendered rows would then move down, the window would be recomputed as if you were further up and reattach to the initial region, the spacer would disappear, and the cycle would repeat. elementsThatOverlapOffsets also assumes offsets increase, so a header at 0 could send its binary search the wrong way for rows just before a header.
The workaround in the repro is a StickyHeaderComponent that forwards the sticky wrapper's layout to onCellLayout and removes onCellLayout from the cloned child. It stops the jumping. If the cause is right, a fix in ScrollViewStickyHeader could look like this:
--- a/packages/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js+++ b/packages/react-native/Libraries/Components/ScrollView/ScrollViewStickyHeader.js
@@ const _onLayout = (event: LayoutChangeEvent) => {
if (child.props.onLayout) {
child.props.onLayout(event);
}
+ // VirtualizedList cells take `onCellLayout` rather than `onLayout`.+ if (child.props.onCellLayout) {+ child.props.onCellLayout(event, child.props.cellKey, child.props.index);+ }
};
@@
{cloneElement(child, {
onLayout: undefined, // we call this manually through our this._onLayout
+ onCellLayout: undefined, // likewise, for VirtualizedList cells
style: styles.fill, // We transfer the child style to the wrapper.
})}
Another option would be for CellRenderer to accept onLayout again, so the existing extraction works as the comment describes.
Steps to reproduce
Scroll down.
Around section 10 to 11, the list starts jumping back and forth.
React Native Version
0.86.3
Affected Platforms
Runtime - Android, Runtime - iOS
Output of npx @react-native-community/cli info
System:
OS: macOS 27.0
CPU: (10) arm64 Apple M1 Pro
Memory: 57.91 MB / 32.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 24.14.1
path: /Users/_/.nvm/versions/node/v24.14.1/bin/node
Yarn:
version: 4.13.0
path: /Users/_/.nvm/versions/node/v24.14.1/bin/yarn
npm:
version: 11.11.0
path: /Users/_/.nvm/versions/node/v24.14.1/bin/npm
Watchman: Not Found
Managers:
CocoaPods:
version: 1.17.0
path: /opt/homebrew/bin/pod
SDKs:
iOS SDK:
Platforms:
- DriverKit 27.0
- iOS 27.0
- macOS 27.0
- tvOS 27.0
- visionOS 27.0
- watchOS 27.0
Android SDK: Not Found
IDEs:
Android Studio: 2025.3 AI-253.31033.145.2533.15113396
Xcode:
version: 27.0/27A266a
path: /usr/bin/xcodebuild
Languages:
Java:
version: 17.0.20.1
path: /usr/bin/javac
Ruby:
version: 4.0.7
path: /opt/homebrew/bin/ruby
npmPackages:
"@react-native-community/cli": Not Found
react:
installed: 19.2.3
wanted: 19.2.3
react-native:
installed: 0.86.3
wanted: 0.86.3
react-native-macos: Not Found
npmGlobalPackages:
"*react-native*": Not Found
Android:
hermesEnabled: Not found
newArchEnabled: Not found
iOS:
hermesEnabled: Not found
newArchEnabled: Not found
Description
In a long
SectionListwithstickySectionHeadersEnabled, the list can break once you've scrolled down a bit. It starts jumping back and forth, flipping between two sets of rows.Claude found what appears to be a fix by patching ScrollViewStickyHeader. This can be flipped on in the reproducer app.
The following is Claude's analysis. I have not verified if any of this is true:
Steps to reproduce
React Native Version
0.86.3
Affected Platforms
Runtime - Android, Runtime - iOS
Output of
npx @react-native-community/cli infoStacktrace or Logs
MANDATORY Reproducer
https://github.com/hzr/jumping-section-list
Screenshots and Videos
Bug appears at section 10
Untitled.mp4