0
# List Components
1
2
High-performance list and virtualization components for handling large datasets efficiently with comprehensive scroll management, pull-to-refresh, and advanced rendering optimizations.
3
4
## FlatList
5
6
A performant list component for rendering simple, flat lists with virtualization, cross-platform support, and extensive customization options.
7
8
```javascript { .api }
9
const FlatList: React.ComponentType<FlatListProps<ItemT>>;
10
```
11
12
**Required Props:**
13
- `data` - Array of items to render
14
- `renderItem` - Function to render each item: `({item, index, separators}) => React.Element`
15
16
**Core Props:**
17
- `keyExtractor` - Extract unique key for each item: `(item, index) => string`
18
- `extraData` - Additional data to trigger re-renders when changed
19
- `horizontal` - Render items horizontally instead of vertically
20
- `numColumns` - Number of columns for grid layout (vertical only)
21
- `initialNumToRender` - Items to render in initial batch
22
- `initialScrollIndex` - Start at specific index (requires `getItemLayout`)
23
- `inverted` - Reverse scroll direction
24
- `removeClippedSubviews` - Optimize performance by removing off-screen views
25
26
**Layout Optimization:**
27
- `getItemLayout` - Provide item dimensions for performance: `(data, index) => {length, offset, index}`
28
- `columnWrapperStyle` - Style for multi-column rows
29
- `ListHeaderComponent` - Component at the top of the list
30
- `ListFooterComponent` - Component at the bottom of the list
31
- `ListEmptyComponent` - Component when list is empty
32
- `ItemSeparatorComponent` - Component between items
33
34
**Scroll Props:**
35
- `onScroll` - Scroll event handler
36
- `onScrollToIndexFailed` - Called when scrollToIndex fails
37
- `scrollEventThrottle` - Scroll event throttling
38
- `showsHorizontalScrollIndicator` - Show horizontal scroll bar
39
- `showsVerticalScrollIndicator` - Show vertical scroll bar
40
41
**Viewability:**
42
- `onViewableItemsChanged` - Called when viewable items change
43
- `viewabilityConfig` - Configuration for viewability callbacks
44
- `viewabilityConfigCallbackPairs` - Multiple viewability configs
45
46
**Methods:**
47
- `scrollToEnd({animated?})` - Scroll to end of list
48
- `scrollToIndex({index, animated?, viewPosition?, viewOffset?})` - Scroll to specific index
49
- `scrollToItem({item, animated?, viewPosition?, viewOffset?})` - Scroll to specific item
50
- `scrollToOffset({offset, animated?})` - Scroll to pixel offset
51
- `recordInteraction()` - Trigger viewability calculations
52
- `flashScrollIndicators()` - Show scroll indicators briefly
53
54
**Usage:**
55
```javascript
56
import { FlatList, Text, View, TouchableOpacity } from "react-native-web";
57
58
// Basic list
59
function BasicList() {
60
const data = [
61
{ id: '1', title: 'First Item' },
62
{ id: '2', title: 'Second Item' },
63
{ id: '3', title: 'Third Item' },
64
];
65
66
const renderItem = ({ item, index }) => (
67
<View style={{
68
padding: 16,
69
borderBottomWidth: 1,
70
borderBottomColor: '#eee'
71
}}>
72
<Text>{item.title}</Text>
73
</View>
74
);
75
76
return (
77
<FlatList
78
data={data}
79
renderItem={renderItem}
80
keyExtractor={item => item.id}
81
/>
82
);
83
}
84
85
// Advanced list with optimizations
86
function OptimizedList() {
87
const [data, setData] = useState(generateLargeDataset(1000));
88
const [refreshing, setRefreshing] = useState(false);
89
90
const getItemLayout = (data, index) => ({
91
length: 80, // Item height
92
offset: 80 * index,
93
index,
94
});
95
96
const renderItem = ({ item, index }) => (
97
<TouchableOpacity
98
style={styles.item}
99
onPress={() => handleItemPress(item)}
100
>
101
<Text style={styles.title}>{item.title}</Text>
102
<Text style={styles.subtitle}>{item.subtitle}</Text>
103
</TouchableOpacity>
104
);
105
106
const renderSeparator = () => (
107
<View style={{ height: 1, backgroundColor: '#e0e0e0' }} />
108
);
109
110
const renderHeader = () => (
111
<View style={styles.header}>
112
<Text style={styles.headerText}>My List</Text>
113
</View>
114
);
115
116
const renderFooter = () => (
117
<View style={styles.footer}>
118
<Text>End of list</Text>
119
</View>
120
);
121
122
const renderEmpty = () => (
123
<View style={styles.empty}>
124
<Text>No items found</Text>
125
</View>
126
);
127
128
const onRefresh = async () => {
129
setRefreshing(true);
130
const newData = await fetchFreshData();
131
setData(newData);
132
setRefreshing(false);
133
};
134
135
return (
136
<FlatList
137
data={data}
138
renderItem={renderItem}
139
keyExtractor={item => item.id}
140
getItemLayout={getItemLayout}
141
ItemSeparatorComponent={renderSeparator}
142
ListHeaderComponent={renderHeader}
143
ListFooterComponent={renderFooter}
144
ListEmptyComponent={renderEmpty}
145
initialNumToRender={10}
146
maxToRenderPerBatch={5}
147
windowSize={10}
148
removeClippedSubviews={true}
149
refreshControl={
150
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
151
}
152
onViewableItemsChanged={({ viewableItems }) => {
153
console.log('Visible items:', viewableItems.map(v => v.key));
154
}}
155
viewabilityConfig={{
156
itemVisiblePercentThreshold: 50
157
}}
158
/>
159
);
160
}
161
162
// Grid layout
163
function GridList() {
164
const data = Array.from({ length: 100 }, (_, i) => ({
165
id: i.toString(),
166
title: `Item ${i + 1}`,
167
color: colors[i % colors.length]
168
}));
169
170
const renderGridItem = ({ item }) => (
171
<View style={[styles.gridItem, { backgroundColor: item.color }]}>
172
<Text style={styles.gridText}>{item.title}</Text>
173
</View>
174
);
175
176
return (
177
<FlatList
178
data={data}
179
renderItem={renderGridItem}
180
keyExtractor={item => item.id}
181
numColumns={2}
182
columnWrapperStyle={styles.gridRow}
183
contentContainerStyle={styles.gridContainer}
184
/>
185
);
186
}
187
188
// Horizontal list
189
function HorizontalList() {
190
const categories = [
191
{ id: '1', name: 'Electronics', image: 'electronics.jpg' },
192
{ id: '2', name: 'Clothing', image: 'clothing.jpg' },
193
{ id: '3', name: 'Books', image: 'books.jpg' },
194
];
195
196
const renderCategory = ({ item }) => (
197
<TouchableOpacity style={styles.categoryCard}>
198
<Image source={{ uri: item.image }} style={styles.categoryImage} />
199
<Text style={styles.categoryName}>{item.name}</Text>
200
</TouchableOpacity>
201
);
202
203
return (
204
<FlatList
205
data={categories}
206
renderItem={renderCategory}
207
keyExtractor={item => item.id}
208
horizontal={true}
209
showsHorizontalScrollIndicator={false}
210
contentContainerStyle={styles.horizontalList}
211
/>
212
);
213
}
214
```
215
216
## SectionList
217
218
A high-performance list component for rendering sectioned data with headers, similar to iOS's UITableView.
219
220
```javascript { .api }
221
const SectionList: React.ComponentType<SectionListProps<ItemT>>;
222
```
223
224
**Required Props:**
225
- `sections` - Array of section objects with `data` property
226
- `renderItem` - Function to render each item
227
- `renderSectionHeader` - Function to render section headers
228
229
**Section Object:**
230
```javascript
231
{
232
title: string,
233
data: Array<ItemT>,
234
key?: string, // Optional, will use index if not provided
235
// Any additional custom properties
236
}
237
```
238
239
**Props:**
240
- `keyExtractor` - Extract key for items
241
- `renderSectionFooter` - Render section footers
242
- `SectionSeparatorComponent` - Component between sections
243
- `ItemSeparatorComponent` - Component between items
244
- `stickySectionHeadersEnabled` - Make headers stick to top
245
- `onScrollToIndexFailed` - Handle scroll failures
246
247
**Usage:**
248
```javascript
249
import { SectionList, Text, View } from "react-native-web";
250
251
function ContactsList() {
252
const sections = [
253
{
254
title: 'A',
255
data: ['Alice', 'Andrew', 'Anna'],
256
},
257
{
258
title: 'B',
259
data: ['Bob', 'Betty', 'Brian'],
260
},
261
{
262
title: 'C',
263
data: ['Charlie', 'Catherine', 'Chris'],
264
},
265
];
266
267
const renderItem = ({ item, section, index }) => (
268
<TouchableOpacity style={styles.contactItem}>
269
<Text style={styles.contactName}>{item}</Text>
270
<Text style={styles.contactSection}>Section: {section.title}</Text>
271
</TouchableOpacity>
272
);
273
274
const renderSectionHeader = ({ section: { title } }) => (
275
<View style={styles.sectionHeader}>
276
<Text style={styles.sectionHeaderText}>{title}</Text>
277
</View>
278
);
279
280
const renderSectionFooter = ({ section }) => (
281
<View style={styles.sectionFooter}>
282
<Text>{section.data.length} contacts</Text>
283
</View>
284
);
285
286
return (
287
<SectionList
288
sections={sections}
289
renderItem={renderItem}
290
renderSectionHeader={renderSectionHeader}
291
renderSectionFooter={renderSectionFooter}
292
keyExtractor={(item, index) => item + index}
293
stickySectionHeadersEnabled={true}
294
ItemSeparatorComponent={() => (
295
<View style={{ height: 1, backgroundColor: '#ccc' }} />
296
)}
297
SectionSeparatorComponent={() => (
298
<View style={{ height: 10, backgroundColor: '#f0f0f0' }} />
299
)}
300
/>
301
);
302
}
303
304
// Advanced sectioned list
305
function GroupedDataList() {
306
const [sections, setSections] = useState([]);
307
308
const groupedSections = useMemo(() => {
309
return data.reduce((acc, item) => {
310
const category = item.category;
311
const existingSection = acc.find(section => section.title === category);
312
313
if (existingSection) {
314
existingSection.data.push(item);
315
} else {
316
acc.push({
317
title: category,
318
data: [item],
319
key: category.toLowerCase()
320
});
321
}
322
323
return acc;
324
}, []);
325
}, [data]);
326
327
const renderItem = ({ item, index, section }) => (
328
<View style={styles.listItem}>
329
<Text style={styles.itemTitle}>{item.title}</Text>
330
<Text style={styles.itemPrice}>${item.price}</Text>
331
</View>
332
);
333
334
const renderSectionHeader = ({ section }) => (
335
<View style={[
336
styles.sectionHeader,
337
{ backgroundColor: section.color || '#f5f5f5' }
338
]}>
339
<Text style={styles.sectionTitle}>{section.title}</Text>
340
<Text style={styles.sectionCount}>
341
{section.data.length} items
342
</Text>
343
</View>
344
);
345
346
return (
347
<SectionList
348
sections={groupedSections}
349
renderItem={renderItem}
350
renderSectionHeader={renderSectionHeader}
351
keyExtractor={(item) => item.id}
352
stickySectionHeadersEnabled={true}
353
/>
354
);
355
}
356
```
357
358
## VirtualizedList
359
360
The base component that FlatList and SectionList are built on, providing maximum flexibility for custom list implementations.
361
362
```javascript { .api }
363
const VirtualizedList: React.ComponentType<VirtualizedListProps>;
364
```
365
366
**Required Props:**
367
- `data` - Data source (can be any type)
368
- `getItem` - Function to get item by index: `(data, index) => ItemT`
369
- `getItemCount` - Function to get total count: `(data) => number`
370
- `renderItem` - Render function for items
371
372
**Advanced Props:**
373
- `CellRendererComponent` - Custom cell renderer
374
- `ListHeaderComponent` - Header component
375
- `ListFooterComponent` - Footer component
376
- `debug` - Enable debug mode
377
- `disableVirtualization` - Disable virtualization for debugging
378
379
**Usage:**
380
```javascript
381
// Custom data source example
382
function CustomVirtualizedList() {
383
// Custom data structure (not an array)
384
const customData = {
385
items: new Map([
386
['key1', { title: 'Item 1', value: 100 }],
387
['key2', { title: 'Item 2', value: 200 }],
388
['key3', { title: 'Item 3', value: 300 }],
389
]),
390
order: ['key1', 'key2', 'key3']
391
};
392
393
const getItem = (data, index) => {
394
const key = data.order[index];
395
return data.items.get(key);
396
};
397
398
const getItemCount = (data) => data.order.length;
399
400
const renderItem = ({ item, index }) => (
401
<View style={styles.customItem}>
402
<Text>{item.title}: {item.value}</Text>
403
</View>
404
);
405
406
return (
407
<VirtualizedList
408
data={customData}
409
initialNumToRender={4}
410
renderItem={renderItem}
411
keyExtractor={(item, index) => customData.order[index]}
412
getItemCount={getItemCount}
413
getItem={getItem}
414
/>
415
);
416
}
417
```
418
419
## RefreshControl
420
421
A component for adding pull-to-refresh functionality to scrollable components.
422
423
```javascript { .api }
424
const RefreshControl: React.ComponentType<RefreshControlProps>;
425
```
426
427
**Props:**
428
- `refreshing` - Whether refresh is in progress (required)
429
- `onRefresh` - Callback when refresh is triggered
430
- `colors` - Array of colors for refresh indicator (Android)
431
- `tintColor` - Color of refresh indicator (iOS)
432
- `title` - Title text during refresh (iOS)
433
- `titleColor` - Title text color (iOS)
434
- `progressBackgroundColor` - Progress background color (Android)
435
- `progressViewOffset` - Progress view offset (Android)
436
- `size` - Size of refresh indicator (Android)
437
- `enabled` - Whether refresh is enabled
438
439
**Usage:**
440
```javascript
441
import { ScrollView, RefreshControl, FlatList } from "react-native-web";
442
443
// With ScrollView
444
function RefreshableScrollView() {
445
const [refreshing, setRefreshing] = useState(false);
446
447
const onRefresh = React.useCallback(async () => {
448
setRefreshing(true);
449
try {
450
await fetchNewData();
451
} finally {
452
setRefreshing(false);
453
}
454
}, []);
455
456
return (
457
<ScrollView
458
refreshControl={
459
<RefreshControl
460
refreshing={refreshing}
461
onRefresh={onRefresh}
462
tintColor="#007AFF"
463
title="Pull to refresh"
464
titleColor="#007AFF"
465
colors={['#007AFF', '#4CD964']}
466
/>
467
}
468
>
469
{/* Content */}
470
</ScrollView>
471
);
472
}
473
474
// With FlatList
475
function RefreshableList() {
476
const [data, setData] = useState([]);
477
const [refreshing, setRefreshing] = useState(false);
478
479
const loadData = async () => {
480
const newData = await api.fetchItems();
481
setData(newData);
482
};
483
484
const onRefresh = async () => {
485
setRefreshing(true);
486
await loadData();
487
setRefreshing(false);
488
};
489
490
return (
491
<FlatList
492
data={data}
493
renderItem={renderItem}
494
keyExtractor={item => item.id}
495
refreshControl={
496
<RefreshControl
497
refreshing={refreshing}
498
onRefresh={onRefresh}
499
colors={['#ff0000', '#00ff00', '#0000ff']}
500
progressBackgroundColor="#ffffff"
501
/>
502
}
503
/>
504
);
505
}
506
```
507
508
## Performance Optimization Tips
509
510
```javascript
511
// 1. Use getItemLayout for fixed-size items
512
const getItemLayout = (data, index) => ({
513
length: ITEM_HEIGHT,
514
offset: ITEM_HEIGHT * index,
515
index,
516
});
517
518
// 2. Optimize renderItem with React.memo
519
const ListItem = React.memo(({ item, onPress }) => (
520
<TouchableOpacity onPress={() => onPress(item.id)}>
521
<Text>{item.title}</Text>
522
</TouchableOpacity>
523
));
524
525
// 3. Use keyExtractor effectively
526
const keyExtractor = (item) => item.id.toString();
527
528
// 4. Configure window size and batch rendering
529
<FlatList
530
windowSize={10} // Items to keep in memory
531
initialNumToRender={10} // Items in initial batch
532
maxToRenderPerBatch={5} // Items per batch
533
removeClippedSubviews={true} // Remove off-screen views
534
/>
535
536
// 5. Avoid inline functions and objects
537
const styles = StyleSheet.create({ /* ... */ });
538
const renderItem = useCallback(({ item }) => (
539
<ListItem item={item} style={styles.item} />
540
), []);
541
```
542
543
## Types
544
545
```javascript { .api }
546
interface FlatListProps<ItemT> extends VirtualizedListProps {
547
data: ItemT[] | null | undefined;
548
renderItem: (info: {item: ItemT, index: number, separators: Separators}) => React.ReactElement | null;
549
keyExtractor?: (item: ItemT, index: number) => string;
550
551
// Layout
552
horizontal?: boolean;
553
numColumns?: number;
554
columnWrapperStyle?: ViewStyle;
555
getItemLayout?: (data: ItemT[] | null | undefined, index: number) => {length: number, offset: number, index: number};
556
557
// Performance
558
initialNumToRender?: number;
559
maxToRenderPerBatch?: number;
560
windowSize?: number;
561
removeClippedSubviews?: boolean;
562
563
// Components
564
ListHeaderComponent?: React.ComponentType | React.ReactElement;
565
ListFooterComponent?: React.ComponentType | React.ReactElement;
566
ListEmptyComponent?: React.ComponentType | React.ReactElement;
567
ItemSeparatorComponent?: React.ComponentType;
568
569
// Viewability
570
onViewableItemsChanged?: (info: {viewableItems: ViewToken[], changed: ViewToken[]}) => void;
571
viewabilityConfig?: ViewabilityConfig;
572
573
// Other
574
extraData?: any;
575
inverted?: boolean;
576
refreshControl?: React.ReactElement;
577
}
578
579
interface SectionListProps<ItemT> extends VirtualizedListProps {
580
sections: SectionListData<ItemT>[];
581
renderItem: (info: {item: ItemT, index: number, section: SectionListData<ItemT>, separators: Separators}) => React.ReactElement | null;
582
renderSectionHeader?: (info: {section: SectionListData<ItemT>}) => React.ReactElement | null;
583
renderSectionFooter?: (info: {section: SectionListData<ItemT>}) => React.ReactElement | null;
584
keyExtractor?: (item: ItemT, index: number) => string;
585
stickySectionHeadersEnabled?: boolean;
586
SectionSeparatorComponent?: React.ComponentType;
587
}
588
589
interface SectionListData<ItemT> {
590
data: ItemT[];
591
key?: string;
592
title?: string;
593
[key: string]: any;
594
}
595
596
interface RefreshControlProps extends ViewProps {
597
refreshing: boolean;
598
onRefresh?: () => void;
599
colors?: string[];
600
enabled?: boolean;
601
progressBackgroundColor?: ColorValue;
602
progressViewOffset?: number;
603
size?: 0 | 1;
604
tintColor?: ColorValue;
605
title?: string;
606
titleColor?: ColorValue;
607
}
608
609
interface ViewToken {
610
item: any;
611
key: string;
612
index: number | null;
613
isViewable: boolean;
614
section?: any;
615
}
616
617
interface ViewabilityConfig {
618
minimumViewTime?: number;
619
viewAreaCoveragePercentThreshold?: number;
620
itemVisiblePercentThreshold?: number;
621
waitForInteraction?: boolean;
622
}
623
```