Optimizing Long Lists in React Native with FlatList and getItemLayout
Learn how to replace ScrollView with FlatList, use getItemLayout for fixed‑height items, and tune windowSize to keep memory low while maintaining smooth scrolling.
23 Feb 2026, 19:59 UTC

The problem: ScrollView chokes on long data sets
When a screen needs to show hundreds or thousands of rows, a plain ScrollView mounts every item at once. This blows up memory usage, drops the frame rate, and can cause the app to hang on low‑end Android devices.
Thesis: FlatList + getItemLayout gives virtualization with predictable performance
FlatList renders only the rows that are visible (plus a configurable buffer). By supplying getItemLayout for items of a known height, we skip the measurement step and let the list calculate positions instantly, which improves both initial load and scroll smoothness.
Section 1: Basic FlatList setup
Start with a simple data array. The keyExtractor prop gives each row a stable identity so React can reuse components instead of recreating them.
import React from 'react';
import { FlatList, Text, View, StyleSheet } from 'react-native';
const DATA = Array.from({ length: 1000 }, (_, i) => ({ id: i, text: `Item #${i}` }));
export default function LongList() {
return (
item.id.toString()}
renderItem={({ item }) => (
{item.text}
)>
);
}
const styles = StyleSheet.create({
item: {
padding: 16,
backgroundColor: '#fafafa',
borderBottomWidth: 1,
borderColor: '#ddd',
},
});
Run the app with npx react-native run-android (or iOS) on a device or emulator. You’ll see the list, but scrolling may feel sluggish because each row’s height is measured on the fly.
Section 2: Adding getItemLayout for fixed‑height rows
If every row has the same height (e.g., 56 px), you can tell FlatList exactly where each item starts:
const ITEM_HEIGHT = 56;
function getItemLayout(data, index) {
return {
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
};
}
<FlatList
data={DATA}
keyExtractor={item => item.id.toString()}
getItemLayout={getItemLayout}
renderItem={({ item }) => (
{item.text}
)}
/>
With getItemLayout, FlatList skips the layout pass for each item, which reduces work on the JS thread.
Section 3: Tuning windowSize and initialNumToRender
Two props control the buffer:
initialNumToRender– how many items are painted before the first frame appears. Raising this (e.g., to 10) can shorten the perceived loading delay.windowSize– the number of rows rendered outside the visible viewport. A larger value reduces the chance of blank spots during fast scrolling but consumes more memory.
Example configuration for a 1000‑item list:
<FlatList
data={DATA}
keyExtractor={item => item.id.toString()}
getItemLayout={getItemLayout}
initialNumToRender={8}
windowSize={5}
renderItem={({ item }) => (
{item.text}
)}
/>
Start with the defaults (initialNumToRender={10}, windowSize={21}) and adjust only if you notice:
- Blank rows while scrolling fast → increase
windowSize. - Long pause before the first frame appears → decrease
initialNumToRender.
Trade‑offs and limitations
Using getItemLayout assumes every item shares the same height. If your rows vary (e.g., multiline text, images), you must either:
- Normalize the height (e.g., truncate text, use a fixed‑size image cache), or
- Fall back to the default layout measurement, which costs a bit more CPU but works for heterogeneous rows.
A large windowSize can cause out‑of‑memory crashes on low‑end Android devices, especially when each row holds heavy assets. Keep an eye on memory usage.
How to verify the improvement
- Open Xcode Instruments (iOS) or Android Studio Profiler (Android).
- Record the Memory track while scrolling through the list.
- Compare the baseline (FlatList without
getItemLayout) to the optimized version. - Enable the React Native Perf Monitor (
⌘+D→ “Perf Monitor”) and watch the FPS gauge; aim for a steady 55‑60 fps. - Add a temporary
console.loginsiderenderItemto confirm that the same component instances are reused as you scroll.
You should see a lower memory footprint and a higher, more stable FPS when getItemLayout is used with a modest windowSize.
Actionable closing
If you’re building a screen that shows more than a few dozen rows, replace ScrollView with FlatList. When the row height is constant, add getItemLayout and start with windowSize={5} and initialNumToRender={8}. Profile memory and FPS on a real device, then tweak the buffer props only if you notice blank spots or slow startup. This approach gives you the best trade‑off between smooth scrolling and low memory consumption on both iOS and Android.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.