WritingReact Native FlatList Performance Optimization: What Actually Works — Clixo
5 min readreact-native, performance, flatlist, mobile, optimization

React Native FlatList Performance Optimization: What Actually Works

Practical techniques to fix slow React Native FlatList scroll performance — FlashList, getItemLayout, memoization, and profiling explained.

Dropped frames during list scrolling are one of the first things users notice and one of the last things developers investigate properly. If your React Native app has a FlatList that stutters, freezes, or blanks out cells while scrolling, the problem is almost always one of a small set of measurable causes — not a fundamental React Native limitation.

This guide covers the techniques that consistently produce the largest improvements, in the order you should apply them.

Profile Before You Touch Code

Every optimization effort should start with a number, not a guess. Open the React Native DevTools Performance tab or enable Systrace and record a scroll session on a mid-tier Android device. Look for two things:

  • JS thread frame time: if frames are being dropped here, the render function is doing too much work
  • UI thread frame time: if drops are here, something is forcing layout measurement on the UI thread

Write down your baseline FPS before making any changes. "Feels slow" is not a metric.

The Highest-Impact React Native FlatList Optimization: getItemLayout

If you know the height of each row in advance, implement getItemLayout. This single prop eliminates the measurement pass that FlatList otherwise runs for every item as it enters the viewport.

getItemLayout={(data, index) => ({
  length: ITEM_HEIGHT,
  offset: ITEM_HEIGHT * index,
  index,
})}

For variable-height lists this is not directly applicable, but for feeds, messages, product grids, and settings screens with fixed rows, this is the change with the highest return per line of code.

Switch to FlashList for Complex Lists

Shopify's FlashList is a drop-in FlatList replacement that uses cell recycling instead of React component recycling. In most scenarios it renders significantly faster than FlatList with equivalent configuration.

The API is intentionally close to FlatList. The key difference is that you must provide estimatedItemSize, which gives FlashList the information it needs to pre-allocate the recycling pool correctly.

import { FlashList } from "@shopify/flash-list";
 
// Replace FlatList with FlashList
// Add estimatedItemSize based on your typical item height

Run the Flashlist performance check (npx @shopify/flash-list check) to confirm your estimations are close — poor estimatedItemSize values reduce the benefit.

Memoize List Item Components

By default, React re-renders a list item component any time the parent re-renders, even if that item's data has not changed. Wrap your item component in React.memo:

const ListItem = React.memo(({ item }) => {
  return (
    // your item JSX
  );
});

Also memoize the renderItem prop itself using useCallback, so FlatList receives a stable function reference:

const renderItem = useCallback(({ item }) => (
  <ListItem item={item} />
), []);

Without useCallback, every parent render creates a new renderItem function reference, which bypasses the React.memo optimization on the item component.

Set the Right FlatList Window Configuration

FlatList renders a window of items around the current scroll position. The defaults are tuned for a general case, not for your specific list. The props that matter most:

  • initialNumToRender: number of items rendered on first paint. Set this to exactly the number visible on screen — not more, not less.
  • maxToRenderPerBatch: number of items rendered per incremental batch during scroll. Lower values reduce JS thread pressure per frame.
  • windowSize: the total render window in multiples of the visible area. Reducing this from the default of 21 to 5-7 cuts memory usage significantly on long lists.
  • removeClippedSubviews: set to true on Android for lists with complex item layouts. Has less impact on iOS.

There is no universal value for these props. Profile after each change.

Avoid Inline Functions and Object Literals in renderItem

Patterns like this create new objects on every render:

// Avoid this
renderItem={({ item }) => <Item style={{ margin: 8 }} onPress={() => navigate(item.id)} />}

Every new object reference breaks shallow comparison in React.memo. Pull styles into a StyleSheet.create call and extract handlers with useCallback.

Keep Images Lightweight

List performance degrades fast when images are large or unoptimized. Use react-native-fast-image for HTTP images — it provides persistent disk caching and avoids the re-fetch and re-decode that the built-in Image component does when an item re-mounts.

Serve images at the size they will be displayed. Sending a 1200px image into a 120px cell is both a bandwidth and a decode-time problem.

Paginate, Do Not Load Everything Upfront

Use the onEndReached and onEndReachedThreshold props to load data in pages. A list with 20-30 visible items and pagination is fundamentally faster than a list with 500 items pre-loaded. No amount of windowing configuration compensates for an unbounded item count.

Avoid Nested FlatLists Where Possible

A FlatList inside a ScrollView disables virtualization on the inner list. The React Native docs explicitly warn against this. If your layout calls for a scrollable container with one or more scrollable lists inside it, refactor to a single FlatList using ListHeaderComponent, ListFooterComponent, and SectionList for grouped data.

Checklist Before You Ship

  • Baseline FPS recorded on a physical mid-tier Android device
  • getItemLayout implemented for fixed-height rows
  • Item components wrapped in React.memo
  • renderItem wrapped in useCallback
  • initialNumToRender matched to visible item count
  • Images served at display size and cached with fast-image
  • Pagination implemented via onEndReached
  • No nested FlatLists inside ScrollViews

Smooth list scrolling is a requirement, not a nice-to-have. If your team is working through a larger performance overhaul — FlatList, animations, cold start, or all three — talk to Clixo about scoping the work.