React-Native FlatList performance problems with large list

Here are some improvements you can do to optimize your flatlist:

  1. Pagination
    • You can paginate your data in your backend and refetch it when your scroll gets closer to the end. onEndReached and onEndReachedThreshold can help you.
  2. Update your React Native version to 49 or latest
    • Fiber 16 has amazing performance improvement which makes everything runs faster and smoother
  3. Use PureComponent for render item
    • PureComponent improves the rendering and memory usage of your component, creating render items as pure gives you a better performance experience
  4. Define getItemLayout prop
    • It improves item rendering since React will previously know its layout definition

Hope it helps


Edit Dec 20 '19: The information on this answer became part of the official docs. You should check it out!


Edit May 26 '18: This answer became a bigger and more complete post on github


If you follow this thread, you'll see that react's team is already aware of this performance issue.

There is no silver bullet for this issue, you have to consider the trade offs of every approach and what you think is a good experience for your audience. But fortunately there are several tweaks you can try and improve your FlatList.


Terms and meanings

There are a lot of terms used (on docs or some issues) that were confusing for me at first. So, let's get this out of the way from the start.

  • VirtualizedList is the component behind FlatList, and is React Native's implementation of the 'virtual list' concept.

  • Performance, in this context, imply a smooth (not choppy) scroll (and navigation in or out of your list) experience.

  • Memory consumption, in this context, is how much information about your list is being stored in memory, which could lead to a app crash.

  • Blank areas means that the VirtualizedList couldn't render your items fast enough, so you enter on a part of your list with non rendered components.

  • Window here is not your viewport but rather, size of the area in which items should be rendered.


Props

One way to improve your FlatList is by tweaking it's props. Here are a list of props that can help your with that.

removeClippedSubviews

You can set the removeClippedSubviews prop to true, which unmount components that are off of the window.

Win: This is very memory friendly, as you will always have a little rendered list.

Trade offs: Be aware, that this implementation can have bugs, such as missing content if you use it on a component that will not unmount (such as a navigation route). It also can be less performant, having choppy scroll animations for big lists with complex items on not-so-good devices, as it make crazy amounts of calculations per scroll.

maxToRenderPerBatch

You can set the maxToRenderPerBatch={number}, which is a VirtualizedList prop that can be passed directly to FlatList. With this, you can control the amount of items rendered per batch, which is the next chunk of items rendered on every scroll.

Win: Setting a bigger number means less visual blank areas when scrolling (a better the fill rate).

Trade offs: More items per batch means less JavaScript performance, which means less responsiveness (clicking a item and opening the detail). If you have a static and non-interactive list, this could be the way to go.

initialNumToRender

You can set the initialNumToRender={number}. This means the initial amount of items to render.

Win: You can set this value to the precise number of items that would cover the screen for every device. This can be a big performance boost when rendering the list component.

Trade offs: You are most likely to see blank areas when setting a low initialNumToRender.

windowSize

You can set the windowSize={number}. The number passed here is a measurement unit where 1 is equivalent to your viewport height. The default value is 21, being 10 viewports above, 10 below, and one in between.

Win: If you're worried mainly about performance, you can set a bigger windowSize so your list will run smoothly and with less blank space. If you're mainly worried about memory consumption, you can set a lower windowSize so your rendered list will be smaller.

Trade offs: For a bigger windowSize, you will have a bigger memory consumption. For a lower windowSize, you will have lower performance and bigger change of seeing blank areas.

legacyImplementation

This prop, when true, make your FlatList rely on the older ListView, instead of VirtualizedList.

Win: This will make your list definitely perform better, as it removes virtualization and render all your items at once.

Trade offs: Your memory consumption goes to the roof and chances are good that a big list (100+) with complex items will crash your app. It also fires a warning that the above tweaks will not work, because you're now on a ListView.

disableVirtualization

You will see people advocation the use of this prop on some issues. But this is deprecated now. This used to do something similar to legacyImplementation.


List items

There are also some win-win strategies that involves your list item components. They are being managed by VirtualizedList a lot, so they need to be fast.

Use simple components

The more complex your components are, the slower they will render. Try to avoid a lot of logic and nesting in your list items. If you are reusing this list item component a lot in your app, create a duplicate just for your big lists and make them with less logic as possible and less nested as possible.

Use light components

The heavier your components are, the slower they render. Avoid heavy images (use a cropped version for list items, as small as possible). Talk to your design team, use as little effects and interactions and information as possible in your list. Save them to your item's detail.

Use shouldComponentUpdate

Implement update verification to your components. React's PureComponent is mostly for when you don't have time to think. If you're reading this, you do have time, so, create the strictest rules for your list item components. If your list is simple enough, you could even use

shouldComponentUpdate() {
  return false
}