FlashList replaces FlatList as recommended list in React Native 0.72+
0 reputation · 29 Oct 2020, 05:47 UTC
0 reputation · 29 Oct 2020, 05:47 UTC
Designing pagination for a large dataset with bounded queries in React Native involves choosing virtualization parameters that keep initial render fast and scrolling stable while avoiding memory pressure on low-end Android.
FlatList exposes windowSize, initialNumToRender and maxToRenderPerBatch to control the sliding window and batching. Since React Native 0.72, documentation recommends FlashList for large lists, which uses a custom layout engine and requires explicit estimatedItemSize for variable-height rows. FlashList measures scroll position on the native UI thread, which changes onEndReached timing compared with FlatList, and React Native 0.74 deprecates VirtualizedList.getScrollRef in favor of FlashList's direct scrollRef for programmatic scroll-to-index.
For a paginated list with variable item heights and deep-link scroll-to-index requirements, what estimatedItemSize tolerance is acceptable before layout shifts or missing items appear? Which onEndReachedThreshold value avoids duplicate fetches with FlashList's native measurement under momentum scroll? Does windowSize tuning remain relevant after migrating to FlashList for memory bounding?
35275 reputation · 29 Oct 2020, 16:04 UTC
estimatedItemSize tolerance: Keep the estimate within 10–20% of the actual average item height. Exceeding this range causes visible layout shifts (jumping scrollbars) or blank cells as FlashList's recycling engine compensates mid-scroll.
onEndReachedThreshold to avoid duplicate fetches: Use a value between 0.1 and 0.5. FlashList measures scroll position on the native UI thread, so the event fires earlier than FlatList's JS-based calculation. Values above 0.5 risk triggering multiple fetches during momentum scroll unless your fetch logic is debounced or guarded.
windowSize relevance after migration: Less critical, but not irrelevant. FlashList recycles cells instead of mounting/unmounting, so the windowSize prop no longer controls how many items exist in the React tree. It still bounds the number of recycled native views held in memory, which matters on low-end Android.
FlashList's layout engine pre-calculates total list height from estimatedItemSize * data.length. When real measurements arrive (via onLayout), the engine reconciles the difference. A 10–20% error keeps the reconciliation smooth; larger errors force the engine to insert or remove multiple cells in a single frame, producing the "jumping" scrollbar and momentary blank slots.
FlatList computes scroll position on the JS thread after each frame. FlashList reads it on the UI thread, so the threshold is reached sooner—especially during momentum scroll when the UI thread continues reporting position after JS is idle. A lower threshold (0.1–0.5) compensates for this earlier signal.
In FlatList, windowSize multiplies the visible viewport to determine how many items stay mounted. In FlashList, the recycle pool size is derived from the visible viewport plus a small buffer; windowSize is accepted for API compatibility but only clamps the maximum pool size. It still helps cap native view allocations on constrained devices.
onLayout in a test run). Set estimatedItemSize to that average.onEndReachedThreshold={0.3} as a starting point. Guard your pagination fetch with a boolean flag (e.g., isFetching) so a second native-thread event cannot start another request.windowSize={10} (or similar) if you observe memory pressure on low-end Android; otherwise the default is fine.VirtualizedList.getScrollRef() calls with flashListRef.current?.scrollRef (React Native 0.74+).If your item heights are highly bimodal (e.g., many 50 px rows mixed with occasional 500 px expandable cards), a single estimatedItemSize cannot stay within 10–20% for both modes. In that case, consider:
getItemLayout for the predictable rows and accepting FlashList's dynamic measurement only for the variable ones.Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 29 Oct 2020, 11:32 UTC
A related follow-up for deep-link scroll-to-index: FlashList's scrollToIndex resolves offsets through its layout engine, which depends on whether rows have been measured. When getItemLayout is omitted and items are variable height, the engine falls back to estimatedItemSize to calculate the target offset. If the target item has not yet been rendered, the list scrolls to the estimated position and corrects after the native layout pass completes.
This means the estimatedItemSize tolerance is effectively tighter for programmatic jumps than for passive scrolling. A 10–20% average error may be acceptable for general browsing, but when jumping deep into a paginated dataset, that percentage compounds across every unmeasured row above the target. Supplying getItemLayout with exact heights eliminates the estimation entirely, though it requires deterministic row heights. If heights are truly variable, verifying the correction behavior on a mid-range Android device is the safest way to confirm the drift is acceptable.
On React Native 0.74+, also confirm your code uses the FlashList scrollRef directly rather than the deprecated VirtualizedList.getScrollRef path.