Illustration for Optimizing React Native Lists for Performance

If you've built a React Native app, you've likely encountered the "white flash" of doom or stuttering scrolls when dealing with large lists. Despite the performance leaps in the New Architecture (Fabric), list virtualization remains one of the trickiest parts of mobile development.

In this post, we're going beyond the basics of keyExtractor and diving into advanced optimization techniques for 2025.

1. The King is Dead, Long Live FlashList

For years, FlatList was the standard. It was good, but it had flaws—mainly that it destroyed views when they went off-screen and recreated them when they came back. This constant allocation and deallocation is expensive.

Enter FlashList by Shopify. If you aren't using it yet, you should be.

FlashList uses cell recycling (similar to RecyclerView on Android or UICollectionView on iOS). Instead of destroying a view, it keeps it in a pool and rebinds it with new data.

import { FlashList } from "@shopify/flash-list";

<FlashList
  data={DATA}
  renderItem={({ item }) => <MyItem item={item} />}
  estimatedItemSize={100} // Crucial for performance!
/>

The estimatedItemSize prop is the secret sauce. It allows FlashList to calculate the scroll content size before rendering, preventing layout jumps.

2. Memoization is Non-Negotiable

React Native renders are fast, but not free. If your list has 100 items and you update one, you don't want to re-render all 100.

Wrap your list item component in React.memo.

const MyItem = React.memo(({ item, onPress }) => {
  return (
    <TouchableOpacity onPress={onPress}>
      <Text>{item.title}</Text>
    </TouchableOpacity>
  );
}, (prevProps, nextProps) => {
  return prevProps.item.id === nextProps.item.id;
});

Gotcha: If you pass an inline arrow function as a prop (e.g., onPress={() => handlePress(item)}), you are creating a new function reference on every render, breaking React.memo. Always use useCallback or pass the ID to the handler in the child component.

3. Optimizing Images

Images are often the culprit for dropped frames.

4. The getItemLayout Hack

If you are stuck with FlatList and your items have a fixed height, getItemLayout is a cheat code. It tells the list exactly where every item is without measuring it.

getItemLayout={(data, index) => (
  {length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
)}

This removes the need for the asynchronous layout pass, making scrolling significantly smoother.

Conclusion

Performance isn't about one big fix; it's about death by a thousand cuts. By switching to FlashList, enforcing memoization, and being disciplined about image loading, you can achieve 60 (or 120) FPS scrolling even on low-end Android devices.