FlatList crashes on low-end Android when windowSize is increased for large dataset pagination
0 reputation · 25 Sept 2025, 13:26 UTC
0 reputation · 25 Sept 2025, 13:26 UTC
When paginating a dataset of more than 1,000 items through FlatList, the goal is to maintain smooth scrolling and responsive onEndReached fetches without triggering memory-related crashes on low-end Android hardware. The windowSize prop expands the render buffer beyond the viewport, but raising it to reduce blank areas during fast scrolling also increases the number of mounted item components. Because memory usage scales with the complexity of the renderItem callback rather than the total length of the data array, even a virtualized list can exhaust available heap if each row contains images or nested layouts.
There is uncertainty around how to bound the query and tune the windowing parameters safely. Values such as 5 and 41 are commonly compared to evaluate scroll smoothness, yet the interaction between windowSize, initialNumToRender, and a heavy renderItem function is not obvious from the documentation alone. Additionally, an incorrectly set onEndReachedThreshold may fire multiple redundant network requests before the previous page resolves, compounding memory and CPU pressure.
What practical upper bound for windowSize is safe when renderItem is moderately complex? Should initialNumToRender be reduced to offset a larger windowSize, or does that trade-off only affect perceived load time? How can onEndReached be guarded so that pagination fetches remain exactly one per page without introducing stale closures?
35275 reputation · 25 Sept 2025, 23:15 UTC
For moderately complex renderItem callbacks on low-end Android devices (roughly 512 MB RAM), a practical upper bound for windowSize is 8 to 12. Values such as 41 will multiply mounted rows far beyond what the heap can sustain and are the likely cause of the OOM crashes. Keep initialNumToRender at 4 to 6; do not reduce it to offset a larger window, because that trade-off mainly introduces blank space during the first scroll rather than reducing steady-state memory. To keep pagination to exactly one request per page, guard onEndReached with a useRef boolean that is set before the async fetch and cleared only in a finally block after the promise settles.
The crash is almost certainly caused by the number of simultaneously mounted cells, not by the length of the data array. FlatList virtualizes the list, but windowSize is measured in viewport heights: each increment adds another screenful of rows. When every row contains images or nested layouts, heap usage scales with mounted row complexity, so a large buffer exhausts memory before the virtualizer can recycle views.
Confirmed behavior: initialNumToRender only affects the initial batch. Lowering it does not reduce the peak memory once scrolling begins; it only shifts blank space from the initial render to the first scroll. A ref-based fetch guard is the standard pattern to avoid stale closures because the ref identity is stable across renders, unlike a state variable captured in a closure.
windowSize={10} as a starting ceiling. If rows are image-heavy, try 5.initialNumToRender at 5 (or one viewport height).removeClippedSubviews on Android so off-screen views are detached.onEndReached handler with a guarded version that tracks a page cursor and an in-flight flag.const isFetching = useRef(false);
const page = useRef(1);
const loadMore = useCallback(async () => {
if (isFetching.current) return;
isFetching.current = true;
try {
const next = await fetchPage(page.current + 1);
page.current += 1;
setItems(prev => [...prev, ...next]);
} finally {
isFetching.current = false;
}
}, []);
<FlatList
data={items}
renderItem={renderItem}
keyExtractor={item => item.id}
windowSize={10}
initialNumToRender={5}
removeClippedSubviews
onEndReached={loadMore}
onEndReachedThreshold={0.5}
/>Profile with Android Studio Memory Profiler while scrolling a list of more than 2,000 items. Allocated memory should plateau rather than climb with each page. Use Perfetto or Systrace to confirm that 90th-percentile frame times stay under 16 ms. During a rapid scroll, log onEndReached calls and verify that exactly one network request fires per page.
Does the crash occur during fast scrolling, or immediately after a page fetch resolves? If it happens only after a fetch, the bottleneck may be response payload size or image decoding rather than windowSize, which would change the recommended fix.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.