Nested horizontal FlatList reports items as viewable while its parent row is outside the screen
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 66/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- javascript, react-native
- Domain
- mobile
Research direction
Start with the mandatory Snack reproducer and the nested FlatList configuration using viewabilityConfig and onViewableItemsChanged. Trace how the inner horizontal list determines viewability when its parent row is outside the device viewport. Done means below-the-fold nested lists no longer report items as viewable, while visible nested items still do so on Android and iOS.
Written by the indexing model from the issue text.
Description
Description
System Info
React Native: 0.86.2
React: 19.2.3
React Native Gradle Plugin: 0.86.2
Node: 22.22.3
Project Node version (.nvmrc): 22.22.3
npm: 10.9.8
Yarn: 3.5.0
OS: Linux x86_64, kernel 6.8.0-136-generic
Android compile SDK: 36
Android target SDK: 36
Android min SDK: 24 (Android 7.0+)
Java: 17.0.19
Hermes: enabled
New Architecture: enabled
Expected behavior
For a horizontal FlatList nested inside a vertically scrolling FlatList, the inner list’s onViewableItemsChanged should not report an item as viewable until
the parent row is visible in the device viewport.
An item should satisfy both conditions:
Visible within the inner horizontal FlatList
AND
The parent row containing that FlatList is visible on screen
Actual behavior
onViewableItemsChanged for a nested horizontal FlatList fires when its parent row mounts, even if the parent row is below the device viewport.
The inner FlatList appears to calculate viewability relative to its own local scroll viewport only. It does not account for the outer vertical FlatList’s
clipping or whether the parent row is actually visible on the screen.
This produces false impression events for analytics: items are marked viewable before the user can see them.
Details
Our application has a vertical FlatList of sections. Several sections contain horizontal FlatLists.
On the initial screen:
Sections 1–4: visible in the device viewport
Sections 5–8: mounted by the outer FlatList but below the device viewport
The nested horizontal FlatLists in Sections 5–8 invoke onViewableItemsChanged immediately, reporting their initial items as viewable before the user scrolls
the outer list.
`import React, { useRef } from 'react';
import { FlatList, StyleSheet, Text, View } from 'react-native';
const SECTION_HEIGHT = 220;
const ITEM_WIDTH = 140;
const sections = Array.from({ length: 8 }, (_, index) => ({
id: section-${index + 1},
title: Section ${index + 1},
}));
const items = Array.from({ length: 8 }, (_, index) => ({
id: item-${index + 1},
title: Item ${index + 1},
}));
function NestedHorizontalList({ sectionId }) {
// These objects must retain their identity for the lifetime of a FlatList.
const viewabilityConfig = useRef({
itemVisiblePercentThreshold: 50,
minimumViewTime: 0,
waitForInteraction: false,
}).current;
const onViewableItemsChanged = useRef(({ viewableItems }) => {
console.log(
[${sectionId}] reported viewable before outer list is scrolled:,
viewableItems.map(({ item }) => item.id).join(', ')
);
}).current;
return (
<FlatList
horizontal
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
{item.title}
)}
viewabilityConfig={viewabilityConfig}
onViewableItemsChanged={onViewableItemsChanged}
showsHorizontalScrollIndicator={false}
/>
);
}
export default function App() {
return (
<FlatList
data={sections}
getItemLayout={(_, index) => ({
length: SECTION_HEIGHT,
offset: SECTION_HEIGHT * index,
index,
})}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
{item.title}
)}
/>
);
}
const styles = StyleSheet.create({
section: {
height: SECTION_HEIGHT,
paddingTop: 16,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#9ca3af',
},
sectionTitle: {
marginHorizontal: 16,
marginBottom: 12,
fontSize: 18,
fontWeight: '600',
},
item: {
width: ITEM_WIDTH,
height: 120,
marginLeft: 12,
alignItems: 'center',
justifyContent: 'center',
borderRadius: 8,
backgroundColor: '#dbeafe',
},
});`
For analytics, this is incorrect: “viewable” should represent visibility to the user, not only visibility within an off-screen nested list container.
Steps to reproduce
- Reproduction steps
- Render a vertical FlatList with multiple fixed-height rows.
- Place a horizontal FlatList inside every row.
- Add onViewableItemsChanged and viewabilityConfig to each inner horizontal FlatList.
- Launch the screen without scrolling.
- Observe that inner lists belonging to below-the-fold parent rows report items as viewable.
React Native Version
0.86.2
Affected Platforms
Runtime - Android, Runtime - iOS
Output of npx @react-native-community/cli info
System:
OS: Linux 6.8 Ubuntu 22.04.5 LTS 22.04.5 LTS (Jammy Jellyfish)
CPU: (16) x64 12th Gen Intel(R) Core(TM) i7-1270P
Memory: 13.94 GB / 30.59 GB
Shell:
version: 5.1.16
path: /bin/bash
Binaries:
Node:
version: 22.22.3
path: /home/user/.nvm/versions/node/v22.22.3/bin/node
Yarn:
version: 3.5.0
path: /home/user/.nvm/versions/node/v22.22.3/bin/yarn
npm:
version: 10.9.8
path: /home/user/.nvm/versions/node/v22.22.3/bin/npm
SDKs:
Android SDK:
API Levels: 28, 30, 31, 32, 33, 34, 35, 36
Build Tools: 36.1.0
System Images: android-36 | Google Play Intel x86_64 Atom
Android NDK: Not required for standard Expo development
Languages:
Java:
version: 17.0.19
npmPackages:
react: 19.2.3
react-native: 0.86.2
expo: ~57.0.7
Android:
hermesEnabled: true
newArchEnabled: true
Stacktrace or Logs
NA
MANDATORY Reproducer
https://snack.expo.dev/@testophobia/-nested-flatlist-viewability-repro
Screenshots and Videos
No response
- Dominant language
- C++
- Stars
- 127k
- Forks
- 25.3k
- PR merge metrics
- No merged PRs in 30d
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from react/react-native
-
Needs: Author Feedback Needs: Repro
Difficulty 1/5 Under an hour Newbie friendliness 92/100
react/react-native#58621 · 1 comment ·
-
Needs: Author Feedback Needs: Repro
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
react/react-native#58610 · 1 comment ·
-
Needs: Triage :mag:
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
react/react-native#58565 · 1 comment · 2 reactions ·
-
Needs: Author Feedback Needs: Repro
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
react/react-native#58555 · 5 comments · 2 reactions ·
-
Needs: Attention Needs: Repro
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
react/react-native#58526 · 2 comments ·
All issues in react/react-native
Similar issues
-
AuTest Bug Tests
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
apache/trafficserver#13714 ·
-
bug build
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
facebookincubator/velox#19143 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
tenstorrent/tt-metal#57393 · 1 comment ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
objectionary/eo-graphs#74 ·