Skill: Higher-Order Lists
Replace ScrollView with FlatList or FlashList for performant large list rendering.
Quick Pattern
Incorrect:
<ScrollView>
{items.map((item) => <Item key={item.id} {...item} />)}
</ScrollView>Correct:
<FlashList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <Item {...item} />}
estimatedItemSize={50}
/>When to Use
- Rendering more than 10-20 items in a list
- List scrolling is choppy or laggy
- App freezes when loading list data
- Memory usage spikes with long lists
Prerequisites
@shopify/flash-listfor FlashList (recommended)- Understanding of list virtualization
Step-by-Step Instructions
1. Identify the Problem
The FPS graph shows a severe performance problem during list rendering:
- FPS starts at ~60 (smooth)
- Drops to ~3 FPS during heavy list operation
- Recovers after rendering completes
// BAD: ScrollView renders ALL items at once
const BadList = ({ items }) => (
<ScrollView>
{items.map((item, index) => (
<View key={index}>
<Text>{item}</Text>
</View>
))}
</ScrollView>
);With 5000 items, this creates 5000 views immediately, causing:
- Multi-second freeze
- FPS drop to 0
- High memory usage
2. Replace with FlatList
import { FlatList } from 'react-native';
const BetterList = ({ items }) => {
const renderItem = ({ item }) => (
<View>
<Text>{item}</Text>
</View>
);
return (
<FlatList
data={items}
renderItem={renderItem}
keyExtractor={(item, index) => index.toString()}
/>
);
};FlatList only renders visible items + buffer (windowing).
3. Optimize FlatList with getItemLayout
For fixed-height items, skip layout measurement:
const ITEM_HEIGHT = 50;
const OptimizedList = ({ items }) => {
const renderItem = ({ item }) => (
<View style={{ height: ITEM_HEIGHT }}>
<Text>{item}</Text>
</View>
);
const getItemLayout = (_, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
});
return (
<FlatList
data={items}
renderItem={renderItem}
keyExtractor={(item, index) => index.toString()}
getItemLayout={getItemLayout}
/>
);
};4. Upgrade to FlashList (Best Performance)
npm install @shopify/flash-listimport { FlashList } from '@shopify/flash-list';
const BestList = ({ items }) => {
const renderItem = ({ item }) => (
<View style={{ height: 50 }}>
<Text>{item}</Text>
</View>
);
return (
<FlashList
data={items}
renderItem={renderItem}
estimatedItemSize={50} // Required for FlashList
/>
);
};FlashList advantages:
- Recycles views instead of creating new ones
- 78/100 vs 25/100 performance score in benchmarks
- Smoother scrolling at ~54 FPS vs lower for FlatList
Code Examples
Variable Height Items
// Calculate average for estimatedItemSize
// Items are 50px, 100px, 150px
// Average: (50 + 100 + 150) / 3 = 100px
<FlashList
data={items}
renderItem={renderItem}
estimatedItemSize={100}
/>Mixed Item Types
<FlashList
data={items}
renderItem={({ item }) => {
if (item.type === 'header') return <Header {...item} />;
if (item.type === 'product') return <Product {...item} />;
return <DefaultItem {...item} />;
}}
getItemType={(item) => item.type} // Helps recycling
estimatedItemSize={80}
/>FlatList Optimizations (if not using FlashList)
<FlatList
data={items}
renderItem={renderItem}
// Performance props
removeClippedSubviews={true}
maxToRenderPerBatch={10}
updateCellsBatchingPeriod={50}
initialNumToRender={10}
windowSize={5}
// Avoid re-renders
keyExtractor={(item) => item.id}
extraData={selectedId} // Only when selection changes
/>Performance Comparison
| Component | 5000 Items Load | Scroll FPS | Memory |
|---|---|---|---|
| ScrollView | 1-3 seconds freeze | < 30 | High |
| FlatList | ~100ms | ~45 | Medium |
| FlashList | ~50ms | ~54 | Low |
Decision Matrix
| Scenario | Recommendation |
|---|---|
| < 20 static items | ScrollView OK |
| 20-100 items | FlatList minimum |
| > 100 items | FlashList |
| Complex item layouts | FlashList with getItemType |
| Fixed height items | Add getItemLayout or estimatedItemSize |
Common Pitfalls
- Inline renderItem functions: Causes re-renders. Define outside or use
useCallback. - Missing keyExtractor: Use unique IDs, not array index when possible.
- Ignoring estimatedItemSize warning: FlashList warns if not set. Always provide it.
- Heavy item components: Keep list items light. Move side effects out.
Related Skills
- js-profile-react.md - Profile list rendering
- js-measure-fps.md - Measure scroll performance