Skip to main content
Base implementation for the more convenient FlatList and SectionList components, which are also better documented. In general, this should only really be used if you need more flexibility than FlatList provides, e.g. for use with immutable data instead of plain arrays. Virtualization massively improves memory consumption and performance of large lists by maintaining a finite render window of active items and replacing all items outside of the render window with appropriately sized blank space.

When to Use VirtualizedList

You should use VirtualizedList instead of FlatList when:
  • You need to work with immutable data structures
  • You need custom data handling logic
  • You need more control over item rendering and measurement
  • FlatList’s API doesn’t meet your specific requirements
For most use cases, use FlatList or SectionList instead.

Example

import React from 'react';
import { VirtualizedList, View, Text, StyleSheet } from 'react-native';

const getItem = (data, index) => ({
  id: Math.random().toString(12).substring(0),
  title: `Item ${index + 1}`,
});

const getItemCount = (data) => 50;

const Item = ({ title }) => (
  <View style={styles.item}>
    <Text style={styles.title}>{title}</Text>
  </View>
);

const App = () => (
  <VirtualizedList
    data={[]}
    initialNumToRender={4}
    renderItem={({ item }) => <Item title={item.title} />}
    keyExtractor={item => item.id}
    getItemCount={getItemCount}
    getItem={getItem}
  />
);

const styles = StyleSheet.create({
  item: {
    backgroundColor: '#f9c2ff',
    height: 150,
    justifyContent: 'center',
    marginVertical: 8,
    marginHorizontal: 16,
    padding: 20,
  },
  title: {
    fontSize: 32,
  },
});

export default App;

Required Props

data
any
required
The data source. Can be any type - you define how to access it via getItem and getItemCount.
getItem
(data: any, index: number) => ItemT
required
A generic accessor for extracting an item from any sort of data blob.
getItemCount
(data: any) => number
required
Determines how many items are in the data blob.
renderItem
(info: ListRenderItemInfo<ItemT>) => React.Element | null
required
Takes an item from data and renders it into the list.

Common Props

keyExtractor
(item: ItemT, index: number) => string
Used to extract a unique key for a given item at the specified index. By default, looks for an id or key property on the item.
initialNumToRender
number
default:10
How many items to render in the initial batch. This should be enough to fill the screen but not much more.
maxToRenderPerBatch
number
default:10
Maximum number of items to render in each incremental render batch.
windowSize
number
default:21
Determines the maximum number of items rendered outside of the visible area, in units of visible lengths. Higher values result in more memory consumption but fewer blank areas when scrolling quickly.
updateCellsBatchingPeriod
number
default:50
Amount of time between low-pri item render batches in milliseconds.
horizontal
boolean
If true, renders items next to each other horizontally instead of stacked vertically.
inverted
boolean
Reverses the direction of scroll. Uses scale transforms of -1.
ListHeaderComponent
React.ComponentType | React.Element
Rendered at the top of all the items.
Rendered at the bottom of all the items.
ListEmptyComponent
React.ComponentType | React.Element
Rendered when the list is empty.
ItemSeparatorComponent
React.ComponentType
Rendered in between each item, but not at the top or bottom.
onEndReached
(info: {distanceFromEnd: number}) => void
Called once when the scroll position gets within onEndReachedThreshold of the rendered content.
onEndReachedThreshold
number
default:2
How far from the end (in units of visible length of the list) the bottom edge must be from the end of the content to trigger the onEndReached callback.
getItemLayout
(data, index) => {length: number, offset: number, index: number}
An optional optimization that lets you skip measurement of dynamic content if you know the size of items ahead of time.
refreshing
boolean
Set this true while waiting for new data from a refresh.
onRefresh
() => void
If provided, a standard RefreshControl will be added for pull-to-refresh functionality.
removeClippedSubviews
boolean
This may improve scroll performance for large lists. Note: May have bugs (missing content) in some circumstances.
viewabilityConfig
ViewabilityConfig
Configuration for determining when items are considered viewable.
onViewableItemsChanged
(info: {viewableItems: Array<ViewToken>, changed: Array<ViewToken>}) => void
Called when the viewability of rows changes.

Methods

scrollToEnd()

scrollToEnd(params?: {animated?: boolean})
Scrolls to the end of the content. May be janky without getItemLayout prop.

scrollToIndex()

scrollToIndex(params: {
  animated?: boolean,
  index: number,
  viewOffset?: number,
  viewPosition?: number,
})
Scrolls to the item at the specified index. Cannot scroll to locations outside the render window without specifying getItemLayout.

scrollToItem()

scrollToItem(params: {
  animated?: boolean,
  item: ItemT,
  viewOffset?: number,
  viewPosition?: number,
})
Requires linear scan through data - use scrollToIndex instead if possible.

scrollToOffset()

scrollToOffset(params: {animated?: boolean, offset: number})
Scroll to a specific content pixel offset in the list.

recordInteraction()

recordInteraction()
Tells the list an interaction has occurred, which should trigger viewability calculations.

flashScrollIndicators()

flashScrollIndicators()
Displays the scroll indicators momentarily.

Performance Optimization

getItemLayout

Providing getItemLayout is crucial for:
  • Using scrollToIndex with items outside the render window
  • Smooth scrolling without blank areas
  • Better overall performance
getItemLayout={(data, index) => ({
  length: ITEM_HEIGHT,
  offset: ITEM_HEIGHT * index,
  index,
})}

windowSize

Controls how many screen lengths of content to render:
  • Smaller values = less memory, more blank areas
  • Larger values = more memory, fewer blank areas
  • Default of 21 means ~10 screen lengths in each direction

removeClippedSubviews

Enables native optimization to unmount components outside the viewport:
  • Can significantly improve performance
  • May have rendering bugs on some platforms
  • Test thoroughly before using in production

Important Caveats

  • Internal state is not preserved when content scrolls out of the render window
  • This is a PureComponent - must pass changes via props
  • Content is rendered asynchronously offscreen
  • By default, looks for a key or id prop on each item
  • More complex than FlatList - use FlatList when possible

Differences from FlatList

  1. Data Access: VirtualizedList uses getItem and getItemCount instead of assuming an array
  2. Flexibility: Works with any data structure, not just arrays
  3. Complexity: More props and configuration required
  4. Use Case: Designed for advanced scenarios where FlatList is insufficient

Build docs developers (and LLMs) love