Skip to main content
Version: 2.x

Performance

Profiling

warning

Before assessing your list's performance, make sure you are in release mode. On Android, you can disable JS dev mode inside the developer menu, whereas you need to run the release configuration on iOS. FlashList can appear to be slower than FlatList in dev mode. The primary reason is a much smaller and fixed window size equivalent. Click here to know more about why you shouldn't profile with dev mode on.

Memoizing props passed to FlashList is more important in v2. v1 was more selective about updating items, but this was often perceived as a bug by developers. We will not follow that approach and will instead allow developers to ensure that props are memoized. We will stop re-renders of children wherever it is obvious.

Writing Performant Components

While FlashList does its best to achieve high performance, it will still perform poorly if your item components are slow to render. In this post, let's dive deeper into how you can remedy this.

Recycling

One important thing to understand is how FlashList works under the hood. When an item gets out of the viewport, instead of being destroyed, the component is re-rendered with a different item prop. When optimizing your item component, try to ensure as few things as possible have to be re-rendered and recomputed when recycling.

Optimizations

There's lots of optimizations that are applicable for any React Native component and which might help render times of your item components as well. Usage of useCallback, useMemo, and useRef is advised - but don't use these blindly, always measure the performance before and after making your changes.

note

Always profile performance in the release mode. FlashList's performance between JS dev and release mode differs greatly.

Remove key prop

warning

Using key prop inside your item and item's nested components will highly degrade performance.

Make sure your item components and their nested components don't have a key prop. Using this prop will lead to FlashList not being able to recycle views, losing all the benefits of using it over FlatList.

Why are keys harmful to FlashList?

FlashList's core performance advantage comes from recycling components instead of creating and destroying them however, 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.

For example, if we had a following item component:

const MyNestedComponent = ({ item }) => {
return <Text key={item.id}>I am nested!</Text>;
};

const MyItem = ({ item }) => {
return (
<View key={item.id}>
<MyNestedComponent item={item} />
<Text>{item.title}</Text>
</View>
);
};

Then the key prop should be removed from both MyItem and MyNestedComponent. It isn't needed and react can alredy take care of updating the components.

const MyNestedComponent = ({ item }) => {
return <Text>I am nested!</Text>;
};

const MyItem = ({ item }) => {
return (
<View>
<MyNestedComponent item={item} />
<Text>{item.title}</Text>
</View>
);
};

There might be cases where React forces you to use key prop, such as when using map. In such circumstances, use useMappingHelper to ensure optimal performance:

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

const MyItem = ({ item }) => {
const { getMappingKey } = useMappingHelper();

return (
<>
{item.users.map((user, index) => (
<Text key={getMappingKey(user.id, index)}>{user.name}</Text>
))}
</>
);
};

The useMappingHelper hook intelligently provides the right key strategy:

  • When inside FlashList: Uses stable keys that don't change during recycling
  • When outside FlashList: Uses the provided item key for proper React reconciliation

This approach ensures that:

  • Components can be recycled properly within FlashList
  • React's reconciliation works correctly
  • Performance remains optimal
info

useMappingHelper should be used whenever you need to map over arrays inside FlashList item components. It automatically handles the complexity of providing recycling-friendly keys.

Difficult calculations

If you do any calculations that might take a lot of resources, consider memoizing it, making it faster, or removing it altogether. The render method of items should be as efficient as possible:

getItemType

If you have different types of cell components and these are vastly different, consider leveraging the getItemType prop. For example, if we were building a messages list, we could write it like this:

// A message can be either a text or an image
enum MessageType {
Text,
Image,
}

interface TextMessage {
text: string;
type: MessageType.Text;
}

interface ImageMessage {
image: ImageSourcePropType;
type: MessageType.Image;
}

type Message = ImageMessage | TextMessage;

const MessageItem = ({ item }: { item: Message }) => {
switch (item.type) {
case MessageType.Text:
return <Text>{item.text}</Text>;
case MessageType.Image:
return <Image source={item.image} />;
}
};

// Rendering the actual messages list
const MessageList = () => {
return <FlashList renderItem={MessageItem} estimatedItemSize={200} />;
};

However, this implementation has one performance drawback. When the list recycles items and the MessageType changes from Text to Image or vice versa, React won't be able to optimize the re-render since the whole render tree of the item component changes. We can fix this by changing the MessageList to this:

const MessageList = () => {
return (
<FlashList
renderItem={MessageItem}
estimatedItemSize={200}
getItemType={(item) => {
return item.type;
}}
/>
);
};

FlashList will now use separate recycling pools based on item.type. That means we will never recycle items of different types, making the re-render faster.

Leaf components

Let's consider the following example:

const MyHeavyComponent = () => {
return ...;
};

const MyItem = ({ item }) => {
return (
<>
<MyHeavyComponent />
<Text>{item.title}</Text>
</>
);
};

Since MyHeavyComponent does not directly depend on the item prop, memo can be used to skip re-rending MyHeavyComponent when the item is recycled and thus re-rendered:

const MyHeavyComponent = () => {
return ...;
};

const MemoizedMyHeavyComponent = memo(MyHeavyComponent);

const MyItem = ({ item }: { item: any }) => {
return (
<>
<MemoizedMyHeavyComponent />
<Text>{item.title}</Text>
</>
);
};