FlashList high-performance list patterns. Use when implementing lists.
This skill inherits all available tools. When active, it can use any tool Claude has access to.
This skill covers Shopify's FlashList for high-performance lists.
Use this skill when:
FLASHLIST ALWAYS - FlashList is 10x faster than FlatList. Use it for all lists.
npm install @shopify/flash-list
import { FlashList } from '@shopify/flash-list';
interface Item {
id: string;
title: string;
}
function ItemList({ items }: { items: Item[] }): React.ReactElement {
return (
<FlashList
data={items}
renderItem={({ item }) => (
<View className="p-4 border-b border-gray-200">
<Text className="text-lg">{item.title}</Text>
</View>
)}
estimatedItemSize={60} // Required!
keyExtractor={(item) => item.id}
/>
);
}
// Required for FlashList to calculate recycling
<FlashList
data={items}
renderItem={renderItem}
estimatedItemSize={80} // Average height of items in pixels
/>
import { useCallback, memo } from 'react';
const ItemCard = memo(function ItemCard({ item }: { item: Item }) {
return (
<View className="p-4">
<Text>{item.title}</Text>
</View>
);
});
function ItemList({ items }: { items: Item[] }): React.ReactElement {
const renderItem = useCallback(
({ item }: { item: Item }) => <ItemCard item={item} />,
[]
);
return (
<FlashList
data={items}
renderItem={renderItem}
estimatedItemSize={60}
/>
);
}
<FlashList
data={items}
renderItem={renderItem}
estimatedItemSize={60}
keyExtractor={(item) => item.id} // Unique stable key
/>
interface ListItem {
id: string;
type: 'header' | 'item' | 'separator';
data: unknown;
}
<FlashList
data={items}
renderItem={({ item }) => {
switch (item.type) {
case 'header':
return <HeaderComponent data={item.data} />;
case 'separator':
return <SeparatorComponent />;
default:
return <ItemComponent data={item.data} />;
}
}}
getItemType={(item) => item.type} // Enables better recycling
estimatedItemSize={60}
/>
<FlashList
data={items}
renderItem={renderItem}
estimatedItemSize={60}
overrideItemLayout={(layout, item) => {
// Set exact size for different item types
if (item.type === 'header') {
layout.size = 100;
} else if (item.type === 'separator') {
layout.size = 20;
} else {
layout.size = 60;
}
}}
/>
<FlashList
data={items}
renderItem={renderItem}
estimatedItemSize={150}
horizontal
/>
<FlashList
data={items}
renderItem={renderItem}
estimatedItemSize={180}
numColumns={2}
/>
import { useState } from 'react';
import { RefreshControl } from 'react-native';
function RefreshableList(): React.ReactElement {
const [refreshing, setRefreshing] = useState(false);
const onRefresh = async () => {
setRefreshing(true);
await fetchNewData();
setRefreshing(false);
};
return (
<FlashList
data={items}
renderItem={renderItem}
estimatedItemSize={60}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
/>
);
}
function InfiniteList(): React.ReactElement {
const [items, setItems] = useState<Item[]>([]);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const loadMore = async () => {
if (loading || !hasMore) return;
setLoading(true);
const newItems = await fetchMoreItems();
if (newItems.length === 0) {
setHasMore(false);
} else {
setItems((prev) => [...prev, ...newItems]);
}
setLoading(false);
};
return (
<FlashList
data={items}
renderItem={renderItem}
estimatedItemSize={60}
onEndReached={loadMore}
onEndReachedThreshold={0.5}
ListFooterComponent={loading ? <ActivityIndicator /> : null}
/>
);
}
<FlashList
data={items}
renderItem={renderItem}
estimatedItemSize={60}
ListEmptyComponent={
<View className="flex-1 items-center justify-center p-8">
<Text className="text-gray-500">No items found</Text>
</View>
}
/>
<FlashList
data={items}
renderItem={renderItem}
estimatedItemSize={60}
ListHeaderComponent={
<View className="p-4 bg-gray-100">
<Text className="text-xl font-bold">Items</Text>
</View>
}
ListFooterComponent={
<View className="p-4">
<Text className="text-gray-500 text-center">End of list</Text>
</View>
}
/>
interface Section {
title: string;
data: Item[];
}
// Use SectionList-like pattern
<FlashList
data={flattenedData}
renderItem={({ item }) => {
if (item.isHeader) {
return <StickyHeader title={item.title} />;
}
return <ItemCard item={item} />;
}}
stickyHeaderIndices={headerIndices}
estimatedItemSize={60}
getItemType={(item) => (item.isHeader ? 'header' : 'item')}
/>
import { useRef } from 'react';
import { FlashList } from '@shopify/flash-list';
function ScrollableList(): React.ReactElement {
const listRef = useRef<FlashList<Item>>(null);
const scrollToTop = () => {
listRef.current?.scrollToOffset({ offset: 0, animated: true });
};
const scrollToIndex = (index: number) => {
listRef.current?.scrollToIndex({ index, animated: true });
};
return (
<>
<FlashList
ref={listRef}
data={items}
renderItem={renderItem}
estimatedItemSize={60}
/>
<Button onPress={scrollToTop}>Scroll to Top</Button>
</>
);
}
<FlashList
data={items}
renderItem={renderItem}
estimatedItemSize={60}
keyExtractor={(item) => item.id}
getItemType={(item) => item.type}
numColumns={1}
horizontal={false}
inverted={false}
showsVerticalScrollIndicator={true}
showsHorizontalScrollIndicator={false}
onEndReached={loadMore}
onEndReachedThreshold={0.5}
refreshControl={<RefreshControl />}
ListHeaderComponent={<Header />}
ListFooterComponent={<Footer />}
ListEmptyComponent={<EmptyState />}
ItemSeparatorComponent={() => <View className="h-px bg-gray-200" />}
/>
// Enable debug logs in development
<FlashList
data={items}
renderItem={renderItem}
estimatedItemSize={60}
// Debug overlay shows recycling info
/>
estimatedItemSize is requiredgetItemType for different item layouts