React Native List Components: FlashList, FlatList, and More

Master React Native list rendering with performance-focused strategies for virtualized lists, from ScrollView basics to FlashList v2 optimization.

From ScrollView to Virtualized Lists

Building performant lists is one of the most common challenges in React Native development. Whether you're displaying a feed of social media posts, a product catalog, or a chat history, the way you render list content directly impacts your app's user experience. Poorly optimized lists lead to janky scrolling, memory issues, and frustrated users.

React Native offers several approaches to list rendering, each designed for different use cases and performance requirements. Understanding when to use each component--and how to optimize it--is essential for building smooth, responsive mobile applications.

This guide explores the evolution of list components in React Native, from the foundational ScrollView to modern high-performance solutions like FlashList, with practical code examples and optimization strategies you can apply immediately.

For teams building mobile applications as part of a broader web development strategy, choosing the right list component impacts both user experience and development velocity.

Understanding ScrollView Limitations

The ScrollView component serves as React Native's most basic scrolling container. It renders all of its children simultaneously, making it straightforward to understand and use for small datasets. When you wrap a collection of items in ScrollView, React Native creates views for every single item, regardless of whether those items are currently visible on screen.

This approach works well for lists with a handful of items--think navigation menus, settings screens, or small form fields. However, as your dataset grows, ScrollView quickly becomes problematic. Rendering hundreds or thousands of views simultaneously consumes significant memory and forces the JavaScript thread to process far more work than necessary. The result is slower initial render times, increased memory pressure, and potential performance degradation during scrolling.

ScrollView also lacks several features that list-based UIs often need, including pull-to-refresh, sticky headers support, and efficient data change handling. For anything beyond trivial list implementations, you'll want to move beyond ScrollView's rendering model.

ScrollView is ideal for:

  • Small datasets (fewer than 20-30 items)
  • Simple linear scrolling without virtualization
  • When all content must render immediately

As noted in the React Native documentation on virtualized lists, ScrollView renders all children at once, which works for small collections but becomes problematic at scale.

The Virtualization Revolution

Virtualized lists fundamentally changed how mobile applications render scrollable content. Rather than rendering all items at once, virtualization techniques render only the items currently visible in the viewport, along with a small buffer above and below to ensure smooth scrolling. As users scroll, items leaving the viewport are unmounted, and new items entering the viewport are mounted and rendered.

This approach dramatically reduces memory consumption and initial render time. A list showing 1,000 items might only render 15-20 at any given moment, resulting in vastly better performance than ScrollView's all-at-once rendering model. Virtualized lists also enable smooth scrolling by reducing the work the JavaScript thread must do during scroll events.

React Native's virtualized list implementation forms the foundation for FlatList, SectionList, and third-party solutions like FlashList. Understanding how these components leverage virtualization helps you make informed decisions about which to use in different scenarios.

According to Shopify's engineering team, virtualized list components represent a critical optimization for mobile applications with large or dynamic datasets.

FlatList: The Built-In Solution

FlatList provides React Native's primary virtualized list component, offering a simple API for rendering scrollable lists of data. It handles virtualization automatically, rendering only visible items and their neighbors while providing useful features like pull-to-refresh, endless scrolling, and item separators.

Basic FlatList Implementation

import { FlatList, Text, View } from 'react-native';

const DATA = Array.from({ length: 1000 }, (_, i) => ({
 id: i.toString(),
 title: `Item ${i + 1}`,
 description: `Description for item ${i + 1}`
}));

function BasicFlatList() {
 const renderItem = ({ item }) => (
 <View style={styles.item}>
 <Text style={styles.title}>{item.title}</Text>
 <Text style={styles.description}>{item.description}</Text>
 </View>
 );

 return (
 <FlatList
 data={DATA}
 renderItem={renderItem}
 keyExtractor={item => item.id}
 />
 );
}

The FlatList component accepts a data prop containing your list items and a renderItem function that defines how each item appears. The keyExtractor function tells FlatList how to generate unique keys for each item, which is essential for efficient reconciliation during updates.

As documented in the React Native FlatList guide, proper key extraction is fundamental to list performance and smooth scrolling behavior.

FlatList Configuration Props

React Native's FlatList exposes numerous configuration props that let you balance performance against visual smoothness:

Key Performance Props

PropDefaultPurpose
removeClippedSubviewstrue (Android)Detaches off-screen views from native hierarchy
maxToRenderPerBatch10Items rendered per scroll batch
updateCellsBatchingPeriod50msDelay between render batches
initialNumToRender10Items to render in initial batch
windowSize21Viewport multiples maintained in memory
<FlatList
 data={DATA}
 renderItem={renderItem}
 keyExtractor={item => item.id}
 maxToRenderPerBatch={15}
 updateCellsBatchingPeriod={50}
 initialNumToRender={10}
/>

The maxToRenderPerBatch prop controls how many items FlatList renders in each batch during scroll events. Increasing this value reduces blank areas while scrolling but may cause dropped frames during heavy scrolling. The default value of 10 provides a balance between fill rate and JavaScript thread responsiveness.

The windowSize prop defines the size of the render window as a multiple of viewport height. The default value of 21 means FlatList maintains roughly 10 viewports above and below the visible area. Larger values reduce blank areas during fast scrolling but increase memory consumption.

Per the React Native FlatList optimization documentation, these configuration options allow developers to fine-tune the balance between visual smoothness and performance for their specific use cases.

FlatList Performance Best Practices

Optimizing FlatList performance involves both component configuration and how you structure your item components:

Keep Components Light

  • Avoid heavy nesting in item components
  • Use React.memo to prevent unnecessary re-renders
  • Minimize complex styling calculations
const ListItem = React.memo(({ title, onPress }) => (
 <TouchableOpacity onPress={onPress}>
 <Text>{title}</Text>
 </TouchableOpacity>
), (prevProps, nextProps) => {
 return prevProps.title === nextProps.title;
});

Image Optimization

Avoid heavy images in list items without proper optimization. Use thumbnail versions for list display, implement proper caching, and consider using community packages like react-native-fast-image for improved image performance. Large images that load slowly can block the JavaScript thread and cause scroll stutter.

Use getItemLayout

When all items have known, uniform dimensions, use the getItemLayout prop to eliminate async layout measurements:

<FlatList
 data={DATA}
 renderItem={renderItem}
 keyExtractor={item => item.id}
 getItemLayout={(data, index) => ({
 length: 80,
 offset: 80 * index,
 index
 })}
/>

As recommended in the React Native FlatList best practices, keeping item components simple and memoized is essential for maintaining smooth scroll performance in production applications.

SectionList for Grouped Data

SectionList extends FlatList's functionality to support grouped data with section headers and optional footers. This is ideal for contacts lists, grouped settings, or any data organized into logical categories.

import { SectionList, Text, View } from 'react-native';

const SECTIONS = [
 {
 title: 'Fruits',
 data: ['Apple', 'Banana', 'Cherry']
 },
 {
 title: 'Vegetables',
 data: ['Carrot', 'Broccoli', 'Spinach']
 }
];

function BasicSectionList() {
 return (
 <SectionList
 sections={SECTIONS}
 keyExtractor={(item, index) => item + index}
 renderItem={({ item }) => <Text>{item}</Text>}
 renderSectionHeader={({ section }) => (
 <Text style={styles.sectionHeader}>{section.title}</Text>
 )}
 />
 );
}

SectionList inherits most of FlatList's performance optimization capabilities, including all the configuration props discussed previously. When using SectionList with variable-height items, consider whether getItemLayout can provide meaningful benefits--it's most effective when item heights are predictable.

For mobile applications displaying categorized content, SectionList provides an elegant solution that maintains the performance benefits of virtualization while supporting complex organizational structures.

FlashList: Shopify's High-Performance Alternative

FlashList emerged from Shopify's need for a list component that could outperform FlatList in their production mobile applications. Released as an open-source library, FlashList provides a drop-in replacement for FlatList with significant performance improvements, particularly for lists with complex item components or variable-height content.

The key innovation FlashList brings is smarter recycling and more efficient estimation. While FlatList requires developers to provide estimatedItemSize and relies on sometimes inaccurate estimates, FlashList's algorithms adapt to actual rendered content and minimize visual artifacts during loading.

According to the LogRocket guide on React Native list components, FlashList has become the go-to choice for teams requiring maximum list performance in production applications.

FlashList Basic Implementation

import { FlashList } from '@shopify/flash-list';

function BasicFlashList() {
 const renderItem = useCallback(({ item }) => (
 <View style={styles.item}>
 <Text style={styles.title}>{item.title}</Text>
 <Text style={styles.description}>{item.description}</Text>
 </View>
 ), []);

 return (
 <FlashList
 data={DATA}
 renderItem={renderItem}
 estimatedItemSize={100}
 />
 );
}

The estimatedItemSize prop provides FlashList with an approximate item height, which it uses to optimize initial rendering. Unlike FlatList's estimation, FlashList's algorithm continuously refines its understanding based on actual rendered content.

As documented in the Shopify FlashList documentation, this adaptive estimation leads to progressively better scroll behavior as users interact with the list.

Optimizing FlashList Item Components

Writing performant item components for FlashList requires understanding how the recycling mechanism works. When an item scrolls out of view, FlashList doesn't destroy it--instead, it re-renders the same component with different data.

Avoid the Key Prop

Crucially: Avoid using the key prop within your item component and its nested children. FlashList manages its own keys for recycling, and explicitly provided keys can interfere with the recycling process.

// ❌ Avoid: key prop in item component
const MyItem = ({ item }) => (
 <View key={item.id}>
 <Text>{item.title}</Text>
 </View>
);

// ✅ Correct: Let FlashList handle keys
const MyItem = ({ item }) => (
 <View>
 <Text>{item.title}</Text>
 </View>
);

Use getItemType for Different Item Types

When rendering different component types, use getItemType to maintain separate recycling pools:

const getItemType = (item) => item.type; // 'text', 'image', 'video'

<FlashList
 data={DATA}
 renderItem={MessageItem}
 getItemType={getItemType}
 estimatedItemSize={200}
/>

According to Shopify's FlashList documentation, the getItemType function allows FlashList to maintain separate recycling pools for each type, preventing incorrect recycling across different item types.

Memoization and Leaf Components

Apply React.memo to item components and their children that don't need to re-render when the list scrolls:

const HeavyComponent = () => (
 <View>
 <ExpensiveChart />
 <ComplexAnimation />
 </View>
);

const MemoizedHeavyComponent = React.memo(HeavyComponent);

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

Components that don't depend on the item prop can be memoized independently to skip unnecessary work during recycling. As recommended in the Shopify FlashList performance guide, this approach significantly reduces the JavaScript workload during scrolling.

FlashList v2: A Complete Rewrite

FlashList v2 represents a fundamental redesign built specifically for React Native's New Architecture. This release eliminates several pain points that developers experienced with v1, most notably the requirement to provide item size estimates.

The New Architecture Advantage

The New Architecture introduced synchronous layout measurements, replacing the asynchronous bridge calls that characterized the old architecture. In v1, measuring a view required a round trip across the JavaScript-native bridge, which could take significant time.

FlashList v2 leverages synchronous measurements to build a complete layout map without relying on estimates. The algorithm uses progressive rendering--mounting only one or two items initially--to build an understanding of actual item dimensions.

Key FlashList v2 Features

  • No estimates required: Eliminated the need for estimatedItemSize
  • Pixel-perfect scrolling: Improved scrollToIndex precision
  • maintainVisibleContentPosition: Auto-enabled to maintain scroll position during content changes
  • Enhanced horizontal lists: Items can have dynamic heights
  • Improved masonry support: Dedicated prop with overrideItemLayout support

According to Shopify Engineering's FlashList v2 announcement, performance benchmarks show up to 50% reduced blank area during scrolling compared to v1, thanks to an adaptive rendering algorithm that considers scroll velocity, direction, and actual render times.

For teams building modern React Native applications, FlashList v2 represents the current state of the art in list rendering performance.

Choosing the Right Component

Decision Framework

ComponentBest ForKey Advantages
ScrollViewSmall datasets (<30 items)Simple, no virtualization overhead
FlatListStandard list implementationsBuilt-in features, pull-to-refresh, endless scroll
SectionListGrouped data with headersInherits FlatList optimizations
FlashListComplex/large listsBetter recycling, estimate-free (v2)

Migration Considerations

Migrating from FlatList to FlashList is straightforward since the APIs are similar. The key differences involve estimatedItemSize configuration and potentially removing any key props from item components.

For FlashList v1 users, the migration to v2 should be relatively painless since v2 maintains API compatibility while eliminating the need for estimates.

As noted in the LogRocket React Native list components guide, the migration path is well-documented and most applications can transition with minimal code changes. Teams should review the official migration guide for any breaking changes specific to their use case.

When choosing components for your mobile development project, consider both current requirements and future scalability. Starting with FlashList v2 for new projects avoids technical debt and provides the best foundation for long-term maintenance.

Performance Optimization Checklist

Keep item components simple

Avoid unnecessary nesting and complex structures

Memoize components

Use React.memo to prevent unnecessary re-renders

Proper key extraction

Use keyExtractor correctly, avoid key props in nested elements

Implement getItemLayout

When item dimensions are known and uniform

Optimize images

Use proper sizing and caching strategies

Configure batch rendering

Tune maxToRenderPerBatch and windowSize

Frequently Asked Questions