FlatList & Large Dataset Optimization

📖 Concept

FlatList is React Native's virtualized list component. It only renders items that are visible on screen (plus a configurable buffer), recycling off-screen items to save memory. However, out-of-the-box FlatList performance can be poor — you need to tune it.

Key FlatList props for performance:

Prop Purpose Recommendation
keyExtractor Unique key for reconciliation Use stable IDs, NEVER index
getItemLayout Skip measurement for fixed-height items Always provide if possible
windowSize Number of viewports to render Default 21, reduce for less memory
maxToRenderPerBatch Items rendered per batch Default 10, increase for faster scroll
initialNumToRender Items rendered on first pass Set to visible count
removeClippedSubviews Detach off-screen views true (but can cause rendering bugs)
updateCellsBatchingPeriod Delay between batch renders Increase for smoother scroll

FlashList (Shopify's replacement): FlashList is a drop-in replacement for FlatList that uses cell recycling instead of unmounting/remounting. It provides 5-10x better performance for large lists.

  • Reuses native views instead of destroying and recreating them
  • Requires estimatedItemSize prop
  • More memory-efficient for large lists
  • Significantly better scroll performance

💻 Code Example

codeTap to expand ⛶
1// === OPTIMIZED FLATLIST ===
2
3import { FlatList, View, Text } from 'react-native';
4
5// 1. Memoized list item
6const ProductItem = React.memo(function ProductItem({
7 item,
8 onPress
9}: {
10 item: Product;
11 onPress: (id: string) => void;
12}) {
13 return (
14 <Pressable onPress={() => onPress(item.id)} style={styles.item}>
15 <FastImage
16 source={{ uri: item.thumbnail }}
17 style={styles.thumbnail}
18 resizeMode="cover"
19 />
20 <View style={styles.info}>
21 <Text style={styles.title}>{item.name}</Text>
22 <Text style={styles.price}>{formatPrice(item.price)}</Text>
23 </View>
24 </Pressable>
25 );
26});
27
28// 2. Optimized FlatList configuration
29const ITEM_HEIGHT = 80;
30const SCREEN_HEIGHT = Dimensions.get('window').height;
31const VISIBLE_ITEMS = Math.ceil(SCREEN_HEIGHT / ITEM_HEIGHT);
32
33function ProductList({ products }: { products: Product[] }) {
34 const onPress = useCallback((id: string) => {
35 navigation.navigate('ProductDetail', { id });
36 }, [navigation]);
37
38 const renderItem = useCallback(({ item }: { item: Product }) => (
39 <ProductItem item={item} onPress={onPress} />
40 ), [onPress]);
41
42 const keyExtractor = useCallback((item: Product) => item.id, []);
43
44 // Fixed height layout — HUGE performance boost
45 const getItemLayout = useCallback((
46 _data: Product[] | null | undefined,
47 index: number
48 ) => ({
49 length: ITEM_HEIGHT,
50 offset: ITEM_HEIGHT * index,
51 index,
52 }), []);
53
54 return (
55 <FlatList
56 data={products}
57 renderItem={renderItem}
58 keyExtractor={keyExtractor}
59 getItemLayout={getItemLayout}
60 // Performance tuning
61 initialNumToRender={VISIBLE_ITEMS}
62 maxToRenderPerBatch={VISIBLE_ITEMS * 2}
63 windowSize={5} // Render 5 viewports (2 above, current, 2 below)
64 removeClippedSubviews={true}
65 // Prevent flash of empty content during fast scroll
66 maintainVisibleContentPosition={{
67 minIndexForVisible: 0,
68 }}
69 />
70 );
71}
72
73// === FLASHLIST (SHOPIFY) — PREFERRED FOR LARGE LISTS ===
74
75import { FlashList } from '@shopify/flash-list';
76
77function OptimizedProductList({ products }: { products: Product[] }) {
78 const renderItem = useCallback(({ item }: { item: Product }) => (
79 <ProductItem item={item} onPress={onPress} />
80 ), [onPress]);
81
82 return (
83 <FlashList
84 data={products}
85 renderItem={renderItem}
86 estimatedItemSize={ITEM_HEIGHT} // Required — enables cell recycling
87 // FlashList handles most optimizations automatically:
88 // - Cell recycling (reuses native views)
89 // - Automatic batching
90 // - Optimized scroll handling
91 />
92 );
93}
94
95// === INFINITE SCROLL WITH PAGINATION ===
96
97function InfiniteList() {
98 const [data, setData] = useState<Product[]>([]);
99 const [page, setPage] = useState(1);
100 const [loading, setLoading] = useState(false);
101 const [hasMore, setHasMore] = useState(true);
102
103 const loadMore = useCallback(async () => {
104 if (loading || !hasMore) return;
105 setLoading(true);
106
107 try {
108 const response = await api.getProducts({ page, limit: 20 });
109 setData(prev => [...prev, ...response.items]);
110 setHasMore(response.hasNextPage);
111 setPage(p => p + 1);
112 } catch (error) {
113 // Don't reset data on error — keep what we have
114 console.error('Failed to load more:', error);
115 } finally {
116 setLoading(false);
117 }
118 }, [page, loading, hasMore]);
119
120 return (
121 <FlashList
122 data={data}
123 renderItem={renderItem}
124 estimatedItemSize={80}
125 onEndReached={loadMore}
126 onEndReachedThreshold={0.5} // Trigger when 50% from bottom
127 ListFooterComponent={loading ? <ActivityIndicator /> : null}
128 />
129 );
130}

🏋️ Practice Exercise

List Optimization Exercises:

  1. Generate a list of 10,000 items and compare scroll performance between ScrollView, FlatList, and FlashList
  2. Implement getItemLayout for a list with variable-height items (using estimated heights)
  3. Build an infinite scroll list with pull-to-refresh and pagination
  4. Profile FlatList render counts with React DevTools — ensure only visible items re-render
  5. Implement a SectionList with sticky headers optimized for performance
  6. Create a horizontal card carousel using FlatList with snap-to-item behavior

⚠️ Common Mistakes

  • Using ScrollView for long lists — renders ALL items at once, crashes with 1000+ items due to memory

  • Not providing getItemLayout for fixed-height items — forces FlatList to measure each item, causing scroll jank

  • Creating new function references inside renderItem — defeats React.memo on list items, causing unnecessary re-renders

  • Setting windowSize too high — renders too many off-screen items, wasting memory and CPU

  • Not handling the 'key' properly — using index causes incorrect state association when data changes

💼 Interview Questions

🎤 Mock Interview

Mock interview is powered by AI for FlatList & Large Dataset Optimization. Login to unlock this feature.