Usage
If you are familiar with FlatList, you already know how to use FlashList. You can try out FlashList by changing the component name.
import React from "react";
import { View, Text, StatusBar } from "react-native";
import { FlashList } from "@shopify/flash-list";
const DATA = [
{
title: "First Item",
},
{
title: "Second Item",
},
];
const MyList = () => {
return (
<FlashList
data={DATA}
renderItem={({ item }) => <Text>{item.title}</Text>}
/>
);
};
Important things to knowβ
To avoid common pitfalls, you can also follow these steps for migrating from FlatList, based on our own experience.
- Simply change from
FlatListtoFlashListand render the list. - Important: Scan your
renderItemhierarchy for explicitkeyprop definitions and remove them. If youβre doing a.map()use our hook calleduseMappingHelper. - Check your
renderItemhierarchy for components that make use ofuseStateand verify whether that state would need to be reset if a different item is passed to that component (see Recycling) - If your list has heterogenous views, pass their types to
FlashListusinggetItemTypeprop to improve performance. - Do not test performance with JS dev mode on. Make sure youβre in release mode.
FlashListcan appear slower while in dev mode due to a small render buffer. - 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.
keyExtractoris important to prevent glitches due to item layout changes when going upwards. We highly recommend having a validkeyExtractorwith v2.- Read about new hooks that simplify recycling and reacting to layout changes:
useLayoutState,useRecyclingState - If you're nesting horizontal FlashLists in vertical lists, we highly recommend the vertical list to be FlashList too. We have optimizations to wait for child layout to complete which can improve load times.
Props
renderItemβ
Required
renderItem: ({ item, index, target, extraData }) => void;
Takes an item from data and renders it into the list. Typical usage:
renderItem = ({item}) => (
<Text>{item.title}</Text>
);
...
<FlashList data={[{title: 'Title Text', key: 'item1'}]} renderItem={renderItem} />
Provides additional metadata like index
item(Object): The item fromdatabeing rendered.index(number): The index corresponding to this item in thedataarray.target(string): FlashList may render your items for multiple reasons.Cell- This is for your list item.Measurement- Might be invoked for size measurement and won't be visible. You can ignore this in analytics.StickyHeader- This is for your sticky header. Use this to change your item's appearance while it's being used as a sticky header.
extraData(Object) - This is the sameextraDataprop that was passed toFlashList.
dataβ
Required
For simplicity, data is a plain array of items of a given type.
data: ItemT[];
CellRendererComponentβ
Each cell is rendered using this element. Can be a React Component Class, or a render function. The root component should always be a CellContainer which is also the default component used. Ensure that the original props are passed to the returned CellContainer. The props contain the following properties:
onLayout: Method for updating data about the realCellContainerlayoutindex: Index of the cell in the list, you can use this to query data if neededstyle: Style ofCellContainer, including:flexDirection: Depends on whether your list is horizontal or verticalposition: Value of this will beabsoluteas that's howFlashListpositions elementsleft: Determines position of the element on x axistop: Determines position of the element on y axiswidth: Determines width of the element (present when list is vertical)height: Determines height of the element (present when list is horizontal)
When using with react-native-reanimated, you can wrap CellContainer in Animated.createAnimatedComponent (this is similar to using Animated.View):
const AnimatedCellContainer = Animated.createAnimatedComponent(CellContainer);
return (
<FlashList
CellRendererComponent={(props) => {
return (
<AnimatedCellContainer {...props} style={...}>
);
}}
/>
);
CellRendererComponent?: React.ComponentType<any> | undefined;
ItemSeparatorComponentβ
Rendered in between each item, but not at the top or bottom. By default, leadingItem and trailingItem (if available) props are provided.
ItemSeparatorComponent?: React.ComponentType<any>;
ListEmptyComponentβ
Rendered when the list is empty. Can be a React Component (e.g. SomeComponent), or a React element (e.g. <SomeComponent />).
ListEmptyComponent?: React.ComponentType<any> | React.ReactElement<any, string | React.JSXElementConstructor<any>>;
ListFooterComponentβ
Rendered at the bottom of all the items. Can be a React Component (e.g. SomeComponent), or a React element (e.g. <SomeComponent />).
ListFooterComponent?: React.ComponentType<any> | React.ReactElement<any, string | React.JSXElementConstructor<any>>;
ListFooterComponentStyleβ
Styling for internal View for ListFooterComponent.
ListFooterComponentStyle?: StyleProp<ViewStyle>;
ListHeaderComponentβ
Rendered at the top of all the items. Can be a React Component (e.g. SomeComponent), or a React element (e.g. <SomeComponent />).
ListHeaderComponent?: React.ComponentType<any> | React.ReactElement<any, string | React.JSXElementConstructor<any>>;
ListHeaderComponentStyleβ
Styling for internal View for ListHeaderComponent.
ListHeaderComponentStyle?: StyleProp<ViewStyle>;
contentContainerStyleβ
contentContainerStyle?: ContentStyle;
export type ContentStyle = Pick<
ViewStyle,
| "backgroundColor"
| "paddingTop"
| "paddingLeft"
| "paddingRight"
| "paddingBottom"
| "padding"
| "paddingVertical"
| "paddingHorizontal"
>;
You can use contentContainerStyle to apply padding that will be applied to the whole content itself. For example, you can apply this padding, so that all of your items have leading and trailing space.
drawDistanceβ
drawDistance?: number;
Draw distance for advanced rendering (in dp/px).
extraDataβ
A marker property for telling the list to re-render (since it implements PureComponent). If any of your renderItem, Header, Footer, etc. functions depend on anything outside of the data prop, stick it here and treat it immutably.
extraData?: any;
horizontalβ
If true, renders items next to each other horizontally instead of stacked vertically. Default is false.
horizontal?: boolean;
initialScrollIndexβ
Instead of starting at the top with the first item, start at initialScrollIndex.
initialScrollIndex?: number;
initialScrollIndexParamsβ
Additional configuration for initialScrollIndex. Use viewOffset to apply an offset to the initial scroll position as defined by initialScrollIndex. Ignored if initialScrollIndex is not set.
initialScrollIndexParams?: { viewOffset?: number };
keyExtractorβ
keyExtractor?: (item: object, index: number) => string;
Used to extract a unique key for a given item at the specified index. Key is used for optimizing performance. Defining keyExtractor is also necessary when doing layout animations to uniquely identify animated components.
maintainVisibleContentPositionβ
maintainVisibleContentPosition?: {
disabled?: boolean;
autoscrollToTopThreshold?: number;
autoscrollToBottomThreshold?: number;
startRenderingFromBottom?: boolean;
};
Configuration for maintaining scroll position when content changes. This is enabled by default to reduce visible glitches.
disabled: Set to true to disable this feature. It's enabled by default.autoscrollToTopThreshold: Automatically scroll to maintain position when content is added at the top.autoscrollToBottomThreshold: Automatically scroll to maintain position when content is added at the bottom.animateAutoScrollToBottom: Scroll with animation wheneverautoScrollToBottomis triggered. Default istrue.startRenderingFromBottom: If true, initial render will start from the bottom, useful for chat interfaces.
Example:
<FlashList
data={chatMessages}
maintainVisibleContentPosition={{
autoscrollToBottomThreshold: 0.2,
startRenderingFromBottom: true,
}}
renderItem={({ item }) => <ChatMessage message={item} />}
/>
masonryβ
masonry?: boolean;
Enable masonry layout for grid-like interfaces with varying item heights. When used with numColumns > 1, this creates a masonry-style layout.
<FlashList
data={data}
masonry
numColumns={3}
renderItem={({ item }) => <MasonryItem item={item} />}
/>
maxItemsInRecyclePoolβ
Maximum number of items in the recycle pool. These are the items that are cached in the recycle pool when they are scrolled off the screen. Unless you have a huge number of item types, you shouldn't need to set this.
Setting this to 0, will disable the recycle pool and items will unmount once they are scrolled off the screen. There's no limit by default.
numColumnsβ
Multiple columns can only be rendered with horizontal={false} and will zig-zag like a flexWrap layout. Items should all be the same height - masonry layouts are not supported.
numColumns?: number;
stickyHeaderConfigβ
Configuration object for sticky header behavior and appearance. All properties are optional.
stickyHeaderConfig?: {
useNativeDriver?: boolean;
offset?: number;
backdropComponent?: React.ComponentType<any> | React.ReactElement | null;
hideRelatedCell?: boolean;
};
useNativeDriverβ
If true, the sticky headers will use native driver for animations. Default is true.
useNativeDriver?: boolean;
offsetβ
Offset from the top of the list where sticky headers should stick.
This is useful when you have a fixed header or navigation bar at the top of your screen
and want sticky headers to appear below it instead of at the very top.
Default is 0.
offset?: number;
backdropComponentβ
Component to render behind sticky headers (e.g., a backdrop or blur effect). Renders in front of the scroll view content but behind the sticky header itself. Useful for creating visual separation or effects like backgrounds with blur.
backdropComponent?: React.ComponentType<any> | React.ReactElement | null;
hideRelatedCellβ
When a sticky header is displayed, the cell associated with it is hidden.
Default is false.
hideRelatedCell?: boolean;
Example:
<FlashList
data={sectionData}
stickyHeaderIndices={[0, 10, 20]}
stickyHeaderConfig={{
useNativeDriver: true,
offset: 50, // Headers stick 50px from top
backdropComponent: <BlurView style={StyleSheet.absoluteFill} />,
hideRelatedCell: true,
}}
renderItem={({ item }) => <ListItem item={item} />}
/>
onChangeStickyIndexβ
Callback invoked when the currently displayed sticky header changes as you scroll. Receives the current sticky header index and the previous sticky header index. This is useful for tracking which header is currently stuck at the top while scrolling. The index refers to the position of the item in your data array that's being used as a sticky header.
onChangeStickyIndex?: (current: number, previous: number) => void;
Example:
<FlashList
data={sectionData}
stickyHeaderIndices={[0, 10, 20]}
onChangeStickyIndex={(current, previous) => {
console.log(`Sticky header changed from ${previous} to ${current}`);
}}
renderItem={({ item }) => <ListItem item={item} />}
/>
onBlankAreaβ
onBlankArea?: (blankAreaEvent: {
offsetStart: number;
offsetEnd: number;
blankArea: number;
}) => void;
FlashList computes blank space that is visible to the user during scrolling or the initial loading of the list.
Values reported:
offsetStart: visible blank space on top of the screen (while going up). If value is greater than 0, it's visible to user.offsetEnd: visible blank space at the end of the screen (while going down). If value is greater than 0, it's visible to user.blankArea: maximum ofoffsetStartandoffsetEnd. You might see negative values indicating that items are rendered outside the list's visible area.
This callback will be triggered even if the blanks are excepted - for example, when the list does not have enough items to fill the screen.
This event isn't synced with onScroll event from the JS layer but works with native methods onDraw (Android) and layoutSubviews (iOS).
onCommitLayoutEffectβ
onCommitLayoutEffect?: () => void;
Called before layout is committed. Can be used to measure list and make changes before paint. Doing setState inside the callback can lead to infinite loops. Make sure FlashList's props are memoized.
onEndReachedβ
onEndReached?: () => void;
Called once when the scroll position gets within onEndReachedThreshold of the rendered content.
onEndReachedThresholdβ
onEndReachedThreshold?: number;
How far from the end (in units of visible length of the list) the bottom edge of the list must be from the end of the content to trigger the onEndReached callback. Thus a value of 0.5 will trigger onEndReached when the end of the content is within half the visible length of the list.
onLoadβ
onLoad: (info: { elapsedTimeInMs: number }) => void;
This event is raised once the list has drawn items on the screen. It also reports elapsedTimeInMs which is the time it took to draw the items. This is required because FlashList doesn't render items in the first cycle. Items are drawn after it measures itself at the end of first render. Please note that the event is not fired if ListEmptyComponent is rendered.
onRefreshβ
onRefresh?: () => void;
If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make sure to also set the refreshing prop correctly.
getItemTypeβ
getItemType?: (
item: T,
index: number,
extraData?: any
) => string | number | undefined;
Allows developers to specify item types. This will improve recycling if you have different types of items in the list. Right type will be used for the right item.Default type is 0. If you don't want to change for an indexes just return undefined. You can see example of how to use this prop here.
This method is called very frequently. Keep it fast.
onStartReachedβ
onStartReached?: () => void;
Called once when the scroll position gets within onStartReachedThreshold of the start of the content. Useful for loading older content in infinite scroll scenarios like chat applications.
<FlashList
data={messageData}
onStartReached={() => loadOlderMessages()}
onStartReachedThreshold={0.1}
renderItem={({ item }) => <MessageItem message={item} />}
/>
onStartReachedThresholdβ
onStartReachedThreshold?: number;
How far from the start (in units of visible length of the list) the top edge of the list must be from the start of the content to trigger the onStartReached callback. Value of 0.5 will trigger onStartReached when the start of the content is within half the visible length of the list from the top.
onViewableItemsChangedβ
interface ViewToken {
index: number;
isViewable: boolean;
item: string;
key: string;
timestamp: number;
}
onViewableItemsChanged?: ((info: {
viewableItems: ViewToken[];
changed: ViewToken[];
}) => void) | null | undefined
Called when the viewability of rows changes, as defined by the viewabilityConfig prop. Array of changed includes ViewTokens that both visible and non-visible items. You can use the isViewable flag to filter the items.
If you are tracking the time a view becomes (non-)visible, use the timestamp property. We make no guarantees that in the future viewability callbacks will be invoked as soon as they happen - for example, they might be deferred until JS thread is less busy.
optimizeItemArrangementβ
optimizeItemArrangement?: boolean;
When enabled with masonry layout, this will try to reduce differences in column height by modifying item order. Default is true.
overrideItemLayoutβ
overrideItemLayout?: (
layout: { span?: number;},
item: T,
index: number,
maxColumns: number,
extraData?: any
) => void;
This method can be used to change column span of an item.
In v2, span is supported, but size estimates are no longer needed or read.
Changing item span is useful when you have grid layouts (numColumns > 1) and you want few items to be bigger than the rest.
<FlashList
data={gridData}
numColumns={2}
overrideItemLayout={(layout, item) => {
layout.span = item.span; // Set span
}}
renderItem={({ item }) => <GridItem item={item} />}
/>
This method is called very frequently. Keep it fast.
overridePropsβ
overrideProps?: object;
We do not recommend using this prop for anything else than debugging. Internal props of the list will be overriden with the provided values.
progressViewOffsetβ
progressViewOffset?: number;
Set this when offset is needed for the loading indicator to show correctly.
refreshControlβ
refreshControl?: React.ReactElement<any, string | React.JSXElementConstructor<any>>;
A custom refresh control element. When set, it overrides the default <RefreshControl> component built internally. The onRefresh and refreshing props are also ignored. Only works for vertical VirtualizedList.
refreshingβ
refreshing?: boolean;
Set this true while waiting for new data from a refresh.
renderScrollComponentβ
import type { ScrollViewProps } from "react-native";
renderScrollComponent?:
| React.ComponentType<ScrollViewProps>
| React.FC<ScrollViewProps>;
Rendered as the main scrollview.
styleβ
style?: StyleProp<ViewStyle>;
Style for the FlashList's parent container. It's highly recommended to avoid adding padding which can impact the size of the ScrollView inside. We operate on the assumption that the size of parent view and ScrollView is the same. In most cases, contentContainerStyle should be enough, so avoid using this.
viewabilityConfigβ
interface ViewabilityConfig: {
minimumViewTime: number;
viewAreaCoveragePercentThreshold: number;
itemVisiblePercentThreshold: number;
waitForInteraction: boolean;
}
viewabilityConfig?: ViewabilityConfig;
viewabilityConfig is a default configuration for determining whether items are viewable.
Changing viewabilityConfig on the fly is not supported
Example:
<FlashList
viewabilityConfig={{
waitForInteraction: true,
itemVisiblePercentThreshold: 50,
minimumViewTime: 1000,
}}
...
minimumViewTimeβ
Minimum amount of time (in milliseconds) that an item must be physically viewable before the viewability callback will be fired. A high number means that scrolling through content without stopping will not mark the content as viewable. The default value is 250. We do not recommend setting much lower values to preserve performance when quickly scrolling.
viewAreaCoveragePercentThresholdβ
Percent of viewport that must be covered for a partially occluded item to count as "viewable", 0-100. Fully visible items are always considered viewable. A value of 0 means that a single pixel in the viewport makes the item viewable, and a value of 100 means that an item must be either entirely visible or cover the entire viewport to count as viewable.
itemVisiblePercentThresholdβ
Similar to viewAreaCoveragePercentThreshold, but considers the percent of the item that is visible, rather than the fraction of the viewable area it covers.
waitForInteractionβ
Nothing is considered viewable until the user scrolls or recordInteraction is called after render.
viewabilityConfigCallbackPairsβ
type ViewabilityConfigCallbackPairs = ViewabilityConfigCallbackPair[];
interface ViewabilityConfigCallbackPair {
viewabilityConfig: ViewabilityConfig;
onViewableItemsChanged:
| ((info: { viewableItems: ViewToken[]; changed: ViewToken[] }) => void)
| null;
}
viewabilityConfigCallbackPairs: ViewabilityConfigCallbackPairs | undefined;
List of ViewabilityConfig/onViewableItemsChanged pairs. A specific onViewableItemsChanged will be called when its corresponding ViewabilityConfig's conditions are met.
Hooks
useLayoutStateβ
const [state, setState] = useLayoutState(initialState);
This is similar to useState but communicates the change in state to FlashList. It's useful if you want to resize a child component based on a local state. Item layout changes will still be detected using onLayout callback in the absence of useLayoutState, which might not look as smooth on a case-by-case basis.
import { useLayoutState } from "@shopify/flash-list";
const MyItem = ({ item }) => {
const [isExpanded, setIsExpanded] = useLayoutState(false);
const height = isExpanded ? 150 : 80;
return (
<Pressable onPress={() => setIsExpanded(!isExpanded)}>
<View style={{ height, padding: 16 }}>
<Text>{item.title}</Text>
</View>
</Pressable>
);
};
useRecyclingStateβ
const [state, setState] = useRecyclingState(
initialState,
dependencies,
resetCallback
);
Similar to useState but accepts a dependency array. On change of deps, the state gets reset without an additional setState call. Useful for maintaining local item state if really necessary. It also has the functionality of useLayoutState built in.
import { useRecyclingState } from "@shopify/flash-list";
const GridItem = ({ item }) => {
const [isExpanded, setIsExpanded] = useRecyclingState(
false,
[item.id],
() => {
// runs on reset. Can be used to reset scroll positions of nested horizontal lists
}
);
const height = isExpanded ? 100 : 50;
return (
<Pressable onPress={() => setIsExpanded(!isExpanded)}>
<View style={{ height, backgroundColor: item.color }}>
<Text>{item.title}</Text>
</View>
</Pressable>
);
};
useMappingHelperβ
const { getMappingKey } = useMappingHelper();
Returns a function that helps create a mapping key for items when using .map() in your render methods. Using this ensures that performance is optimal for FlashList by providing consistent keys that work with the recycling system.
The getMappingKey function takes two parameters:
index: The index of the item in the arrayitemKey: A unique identifier for the item (string, number, or bigint)
It returns the appropriate key value to use in the key prop based on the current context.
Basic usage:
import { useMappingHelper } from "@shopify/flash-list";
const MyComponent = ({ items }) => {
const { getMappingKey } = useMappingHelper();
return (
<View>
{items.map((item, index) => (
<Text key={getMappingKey(item.id, index)}>{item.title}</Text>
))}
</View>
);
};
When to use it:
- When mapping over arrays to create lists of components inside FlashList items
- When building nested components that render multiple items from an array
- To ensure consistent key generation that works well with FlashList's recycling system
useFlashListContextβ
Exposes helpers to easily access ref of FlashList. It also exposes ref of ScrollView. Ideal for use within child components or CellRendererComponent.
FlashList methods
prepareForLayoutAnimationRender()β
prepareForLayoutAnimationRender(): void;
Run this method before running layout animations, such as when animating an element when deleting it. This method disables recycling for the next frame so that layout animations run well.
Avoid using this when making large changes to the data as the list might draw too much to run animations since the method disables recycling temporarily. Single item insertions or deletions should animate smoothly. The render after animation will enable recycling again and you can stop avoiding making large data changes.
recordInteraction()β
recordInteraction();
Tells the list an interaction has occurred, which should trigger viewability calculations, e.g. if waitForInteractions is true and the user has not scrolled. You should typically call recordInteraction() when user for example taps on an item or invokes a navigation action.
recomputeViewableItems()β
recomputeViewableItems();
Retriggers viewability calculations. Useful to imperatively trigger viewability calculations.
scrollToEnd()β
scrollToEnd?: (params?: { animated?: boolean | null | undefined });
Scrolls to the end of the content.
scrollToIndex()β
scrollToIndex(params: {
animated?: boolean | null | undefined;
index: number;
viewOffset?: number | undefined;
viewPosition?: number | undefined;
});
Scroll to a given index.
scrollToItem()β
scrollToItem(params: {
animated?: boolean | null | undefined;
item: any;
viewPosition?: number | undefined;
});
Scroll to a given item.
scrollToOffset()β
scrollToOffset(params: {
animated?: boolean | null | undefined;
offset: number;
});
Scroll to a specific content pixel offset in the list.
Param offset expects the offset to scroll to. In case of horizontal is true, the offset is the x-value, in any other case the offset is the y-value.
Param animated (false by default) defines whether the list should do an animation while scrolling.
getVisibleIndices()β
Returns an array of indices that are currently visible in the list.
getVisibleIndices(): number[];
getLayout()β
Returns the current layout information for the list.
getLayout(): { x: number, y: number, width: number; height: number };
flashScrollIndicators()β
Shows the scroll indicators momentarily.
flashScrollIndicators(): void;
getNativeScrollRef()β
Returns a reference to the underlying scroll view.
getNativeScrollRef(): React.RefObject<CompatScroller>;
getScrollResponder()β
Returns the scroll responder of the underlying scroll view.
getScrollResponder(): any;
getScrollableNode()β
Returns the native scrollable node of the underlying scroll view.
getScrollableNode(): any;
scrollToTop()β
Scrolls to the top of the list.
scrollToTop(params?: { animated?: boolean }): void;
getFirstItemOffset()β
Returns the offset of the first item (useful for calculating header size or top padding).
getFirstItemOffset(): number;
getWindowSize()β
Returns the current rendered dimensions of the list.
getWindowSize(): { width: number, height: number };
getFirstVisibleIndex()β
Returns the index of the first visible item in the list.
getFirstVisibleIndex(): number;
ScrollView props
FlashList, as FlatList, uses ScrollView under the hood. You can take a look into the React Native documentation for ScrollView to see the exhaustive list of props.
Unsupported FlatList props
The following props from FlatList are currently not implemented:
There are also FlatList props that would bring no value if ported to FlashList due to the differences in their underlying implementation:
disableVirtualizationgetItemLayoutinitialNumToRendermaxToRenderPerBatchrecordInteractionsetNativePropsupdateCellsBatchingPeriodonScrollToIndexFailedwindowSize
We don't currently plan to implement these props.