This guide helps you diagnose and fix common issues when working with FlashList.
List Appears Slow in Development Mode
Before assessing your list’s performance, make sure you are in release mode.
FlashList can appear to be slower than FlatList in dev mode. The primary reason is a much smaller and fixed window size. On Android, you can disable JS dev mode inside the developer menu, whereas you need to run the release configuration on iOS.
Solution:
# iOS
react-native run-ios --configuration Release
# Android - disable JS dev mode in developer menu
# Or run release build
cd android && ./gradlew assembleRelease
Items Are Slow to Render
If your list is slow even in release mode, the issue is likely with your item components.
Check for these common issues:
- Using
key prop in item components - This prevents recycling
- Heavy calculations in render - Move to
useMemo or compute outside render
- Missing
getItemType - Use for heterogeneous lists
- Not memoizing child components - Use
memo() for components that don’t depend on item data
See the Performance guide for detailed solutions.
Blank areas during scrolling usually indicate performance problems with your item components.
Common causes:
- Item components are too complex and slow to render
- Heavy computations in
renderItem
- Images loading slowly without placeholders
- Using non-memoized callbacks
Solutions:
// 1. Simplify item components
const MyItem = memo(({ item }) => {
// Keep this as simple as possible
return <Text>{item.title}</Text>;
});
// 2. Use getItemType for different item types
<FlashList
getItemType={(item) => item.type}
// ...
/>
// 3. Add image placeholders
<Image
source={{ uri: item.imageUrl }}
defaultSource={require('./placeholder.png')}
/>
Recycling Issues
This happens when components have local state that isn’t properly reset during recycling.
Problem:
// Wrong - state persists when recycled
const MyItem = ({ item }) => {
const [isExpanded, setIsExpanded] = useState(false);
return <View>{/* ... */}</View>;
};
Solution:
Use useRecyclingState hook:
import { useRecyclingState } from '@shopify/flash-list';
const MyItem = ({ item }) => {
// State resets when item.id changes
const [isExpanded, setIsExpanded] = useRecyclingState(
false,
[item.id],
() => {
// Optional reset callback
console.log('State reset');
}
);
return <View>{/* ... */}</View>;
};
Using key prop inside your item and item’s nested components will highly degrade performance.
When you add a key prop that changes between different data items, React treats the component as entirely different and forces a complete re-creation of the component tree, preventing recycling.
Problem:
const MyItem = ({ item }) => {
return (
<View key={item.id}> {/* Don't do this! */}
<Text>{item.title}</Text>
</View>
);
};
Solution:
Remove the key prop. FlashList handles keys automatically. If you’re using .map() inside your item, use useMappingHelper:
import { useMappingHelper } from '@shopify/flash-list';
const MyItem = ({ item }) => {
const { getMappingKey } = useMappingHelper();
return (
<View>
{item.users.map((user, index) => (
<Text key={getMappingKey(user.id, index)}>
{user.name}
</Text>
))}
</View>
);
};
Layout Issues
Items Not Appearing or Layout Incorrect
Check these common issues:
- Missing parent container dimensions
// FlashList needs a parent with defined dimensions
<View style={{ flex: 1 }}> {/* Important! */}
<FlashList data={data} renderItem={renderItem} />
</View>
Items Resizing or Jumping During Layout Changes
Use the useLayoutState hook when you need to change item dimensions based on local state:
import { useLayoutState } from '@shopify/flash-list';
const MyItem = ({ item }) => {
const [isExpanded, setIsExpanded] = useLayoutState(false);
const height = isExpanded ? 150 : 80;
return (
<Pressable onPress={() => setIsExpanded(!isExpanded)}>
<View style={{ height }}>
<Text>{item.title}</Text>
</View>
</Pressable>
);
};
Horizontal List Height Not Matching Content
If using a header or fixed size with horizontal lists, the list assumes a fixed height. To have the list match the tallest item, render the header as the first item instead:
const data = [{ type: 'header' }, ...items];
<FlashList
horizontal
data={data}
getItemType={(item) => item.type}
renderItem={({ item }) => {
if (item.type === 'header') return <Header />;
return <Item item={item} />;
}}
/>
Testing Issues
All Items Mount in Jest Tests
By default, FlashList will mount all items in the test environment.
Solution:
Add the FlashList jest setup to your jest-setup.js:
require('@shopify/flash-list/jestSetup');
Ensure your jest.config.js references the setup file:
module.exports = {
preset: 'react-native',
setupFiles: ['./jest-setup.js'],
};
Error Messages
”Horizontal prop cannot be toggled”
You cannot dynamically change the horizontal prop. Use a key to force recreation:
<FlashList
key={isHorizontal ? 'h' : 'v'}
horizontal={isHorizontal}
// ...
/>
“Masonry and horizontal props are incompatible”
Masonry layout only works with vertical lists. Remove either masonry or horizontal:
// Correct
<FlashList
masonry
numColumns={2}
// horizontal={false} is implied
/>
“numColumns and horizontal props are incompatible”
Multiple columns only work with vertical lists:
// Correct
<FlashList
numColumns={3}
// horizontal={false} is implied
/>
Sticky headers only work with vertical lists. Remove stickyHeaderIndices or stickyHeaderConfig for horizontal lists.
”FlashList v2 is only supported on new architecture”
FlashList v2 requires React Native’s new architecture. Either:
- Enable the new architecture in your app
- Use FlashList v1 for the old architecture
”Exceeded max renders without commit”
This warning indicates duplicate keys or nested ScrollView issues.
This might mean:
- Duplicate keys - Check your
keyExtractor returns unique values:
<FlashList
keyExtractor={(item, index) => `${item.id}-${index}`}
// ...
/>
- Nested in ScrollView - Don’t nest FlashList inside ScrollView:
// Wrong
<ScrollView>
<FlashList data={data} renderItem={renderItem} />
</ScrollView>
// Correct
<FlashList
data={data}
renderItem={renderItem}
ListHeaderComponent={<OtherContent />}
/>
For precise scrolling, especially in RTL horizontal lists or with initialScrollIndex, ensure:
- Don’t use padding in
contentContainerStyle for RTL horizontal lists
- Define a proper
keyExtractor:
<FlashList
keyExtractor={(item) => item.id}
// ...
/>
Make sure:
keyExtractor is properly defined
- For horizontal RTL lists, avoid padding in
contentContainerStyle
<FlashList
initialScrollIndex={10}
keyExtractor={(item) => item.id}
// ...
/>
Data Updates Not Reflecting
List Not Updating When Data Changes
Use extraData to tell FlashList about dependencies:
const [selectedId, setSelectedId] = useState(null);
<FlashList
data={data}
extraData={selectedId} // Include external dependencies
renderItem={({ item }) => (
<Item
item={item}
isSelected={item.id === selectedId}
/>
)}
/>
Items Not Re-rendering on Prop Changes
Memoizing props passed to FlashList is more important in v2. Make sure you’re memoizing callbacks:
const renderItem = useCallback(({ item }) => {
return <MyItem item={item} />;
}, []);
const keyExtractor = useCallback((item) => item.id, []);
<FlashList
data={data}
renderItem={renderItem}
keyExtractor={keyExtractor}
/>
Still Having Issues?
If you can’t find a solution here:
- Check the Known Issues page
- Search existing GitHub issues
- Ask on the Discord community
- Create a new issue with a minimal reproduction