React Native for Web is a comprehensive compatibility library that enables React Native components and APIs to run seamlessly on web browsers using React DOM.
—
High-performance list and virtualization components for handling large datasets efficiently with comprehensive scroll management, pull-to-refresh, and advanced rendering optimizations.
A performant list component for rendering simple, flat lists with virtualization, cross-platform support, and extensive customization options.
const FlatList: React.ComponentType<FlatListProps<ItemT>>;Required Props:
data - Array of items to renderrenderItem - Function to render each item: ({item, index, separators}) => React.ElementCore Props:
keyExtractor - Extract unique key for each item: (item, index) => stringextraData - Additional data to trigger re-renders when changedhorizontal - Render items horizontally instead of verticallynumColumns - Number of columns for grid layout (vertical only)initialNumToRender - Items to render in initial batchinitialScrollIndex - Start at specific index (requires getItemLayout)inverted - Reverse scroll directionremoveClippedSubviews - Optimize performance by removing off-screen viewsLayout Optimization:
getItemLayout - Provide item dimensions for performance: (data, index) => {length, offset, index}columnWrapperStyle - Style for multi-column rowsListHeaderComponent - Component at the top of the listListFooterComponent - Component at the bottom of the listListEmptyComponent - Component when list is emptyItemSeparatorComponent - Component between itemsScroll Props:
onScroll - Scroll event handleronScrollToIndexFailed - Called when scrollToIndex failsscrollEventThrottle - Scroll event throttlingshowsHorizontalScrollIndicator - Show horizontal scroll barshowsVerticalScrollIndicator - Show vertical scroll barViewability:
onViewableItemsChanged - Called when viewable items changeviewabilityConfig - Configuration for viewability callbacksviewabilityConfigCallbackPairs - Multiple viewability configsMethods:
scrollToEnd({animated?}) - Scroll to end of listscrollToIndex({index, animated?, viewPosition?, viewOffset?}) - Scroll to specific indexscrollToItem({item, animated?, viewPosition?, viewOffset?}) - Scroll to specific itemscrollToOffset({offset, animated?}) - Scroll to pixel offsetrecordInteraction() - Trigger viewability calculationsflashScrollIndicators() - Show scroll indicators brieflyUsage:
import { FlatList, Text, View, TouchableOpacity } from "react-native-web";
// Basic list
function BasicList() {
const data = [
{ id: '1', title: 'First Item' },
{ id: '2', title: 'Second Item' },
{ id: '3', title: 'Third Item' },
];
const renderItem = ({ item, index }) => (
<View style={{
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#eee'
}}>
<Text>{item.title}</Text>
</View>
);
return (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
);
}
// Advanced list with optimizations
function OptimizedList() {
const [data, setData] = useState(generateLargeDataset(1000));
const [refreshing, setRefreshing] = useState(false);
const getItemLayout = (data, index) => ({
length: 80, // Item height
offset: 80 * index,
index,
});
const renderItem = ({ item, index }) => (
<TouchableOpacity
style={styles.item}
onPress={() => handleItemPress(item)}
>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.subtitle}>{item.subtitle}</Text>
</TouchableOpacity>
);
const renderSeparator = () => (
<View style={{ height: 1, backgroundColor: '#e0e0e0' }} />
);
const renderHeader = () => (
<View style={styles.header}>
<Text style={styles.headerText}>My List</Text>
</View>
);
const renderFooter = () => (
<View style={styles.footer}>
<Text>End of list</Text>
</View>
);
const renderEmpty = () => (
<View style={styles.empty}>
<Text>No items found</Text>
</View>
);
const onRefresh = async () => {
setRefreshing(true);
const newData = await fetchFreshData();
setData(newData);
setRefreshing(false);
};
return (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={item => item.id}
getItemLayout={getItemLayout}
ItemSeparatorComponent={renderSeparator}
ListHeaderComponent={renderHeader}
ListFooterComponent={renderFooter}
ListEmptyComponent={renderEmpty}
initialNumToRender={10}
maxToRenderPerBatch={5}
windowSize={10}
removeClippedSubviews={true}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
onViewableItemsChanged={({ viewableItems }) => {
console.log('Visible items:', viewableItems.map(v => v.key));
}}
viewabilityConfig={{
itemVisiblePercentThreshold: 50
}}
/>
);
}
// Grid layout
function GridList() {
const data = Array.from({ length: 100 }, (_, i) => ({
id: i.toString(),
title: `Item ${i + 1}`,
color: colors[i % colors.length]
}));
const renderGridItem = ({ item }) => (
<View style={[styles.gridItem, { backgroundColor: item.color }]}>
<Text style={styles.gridText}>{item.title}</Text>
</View>
);
return (
<FlatList
data={data}
renderItem={renderGridItem}
keyExtractor={item => item.id}
numColumns={2}
columnWrapperStyle={styles.gridRow}
contentContainerStyle={styles.gridContainer}
/>
);
}
// Horizontal list
function HorizontalList() {
const categories = [
{ id: '1', name: 'Electronics', image: 'electronics.jpg' },
{ id: '2', name: 'Clothing', image: 'clothing.jpg' },
{ id: '3', name: 'Books', image: 'books.jpg' },
];
const renderCategory = ({ item }) => (
<TouchableOpacity style={styles.categoryCard}>
<Image source={{ uri: item.image }} style={styles.categoryImage} />
<Text style={styles.categoryName}>{item.name}</Text>
</TouchableOpacity>
);
return (
<FlatList
data={categories}
renderItem={renderCategory}
keyExtractor={item => item.id}
horizontal={true}
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.horizontalList}
/>
);
}A high-performance list component for rendering sectioned data with headers, similar to iOS's UITableView.
const SectionList: React.ComponentType<SectionListProps<ItemT>>;Required Props:
sections - Array of section objects with data propertyrenderItem - Function to render each itemrenderSectionHeader - Function to render section headersSection Object:
{
title: string,
data: Array<ItemT>,
key?: string, // Optional, will use index if not provided
// Any additional custom properties
}Props:
keyExtractor - Extract key for itemsrenderSectionFooter - Render section footersSectionSeparatorComponent - Component between sectionsItemSeparatorComponent - Component between itemsstickySectionHeadersEnabled - Make headers stick to toponScrollToIndexFailed - Handle scroll failuresUsage:
import { SectionList, Text, View } from "react-native-web";
function ContactsList() {
const sections = [
{
title: 'A',
data: ['Alice', 'Andrew', 'Anna'],
},
{
title: 'B',
data: ['Bob', 'Betty', 'Brian'],
},
{
title: 'C',
data: ['Charlie', 'Catherine', 'Chris'],
},
];
const renderItem = ({ item, section, index }) => (
<TouchableOpacity style={styles.contactItem}>
<Text style={styles.contactName}>{item}</Text>
<Text style={styles.contactSection}>Section: {section.title}</Text>
</TouchableOpacity>
);
const renderSectionHeader = ({ section: { title } }) => (
<View style={styles.sectionHeader}>
<Text style={styles.sectionHeaderText}>{title}</Text>
</View>
);
const renderSectionFooter = ({ section }) => (
<View style={styles.sectionFooter}>
<Text>{section.data.length} contacts</Text>
</View>
);
return (
<SectionList
sections={sections}
renderItem={renderItem}
renderSectionHeader={renderSectionHeader}
renderSectionFooter={renderSectionFooter}
keyExtractor={(item, index) => item + index}
stickySectionHeadersEnabled={true}
ItemSeparatorComponent={() => (
<View style={{ height: 1, backgroundColor: '#ccc' }} />
)}
SectionSeparatorComponent={() => (
<View style={{ height: 10, backgroundColor: '#f0f0f0' }} />
)}
/>
);
}
// Advanced sectioned list
function GroupedDataList() {
const [sections, setSections] = useState([]);
const groupedSections = useMemo(() => {
return data.reduce((acc, item) => {
const category = item.category;
const existingSection = acc.find(section => section.title === category);
if (existingSection) {
existingSection.data.push(item);
} else {
acc.push({
title: category,
data: [item],
key: category.toLowerCase()
});
}
return acc;
}, []);
}, [data]);
const renderItem = ({ item, index, section }) => (
<View style={styles.listItem}>
<Text style={styles.itemTitle}>{item.title}</Text>
<Text style={styles.itemPrice}>${item.price}</Text>
</View>
);
const renderSectionHeader = ({ section }) => (
<View style={[
styles.sectionHeader,
{ backgroundColor: section.color || '#f5f5f5' }
]}>
<Text style={styles.sectionTitle}>{section.title}</Text>
<Text style={styles.sectionCount}>
{section.data.length} items
</Text>
</View>
);
return (
<SectionList
sections={groupedSections}
renderItem={renderItem}
renderSectionHeader={renderSectionHeader}
keyExtractor={(item) => item.id}
stickySectionHeadersEnabled={true}
/>
);
}The base component that FlatList and SectionList are built on, providing maximum flexibility for custom list implementations.
const VirtualizedList: React.ComponentType<VirtualizedListProps>;Required Props:
data - Data source (can be any type)getItem - Function to get item by index: (data, index) => ItemTgetItemCount - Function to get total count: (data) => numberrenderItem - Render function for itemsAdvanced Props:
CellRendererComponent - Custom cell rendererListHeaderComponent - Header componentListFooterComponent - Footer componentdebug - Enable debug modedisableVirtualization - Disable virtualization for debuggingUsage:
// Custom data source example
function CustomVirtualizedList() {
// Custom data structure (not an array)
const customData = {
items: new Map([
['key1', { title: 'Item 1', value: 100 }],
['key2', { title: 'Item 2', value: 200 }],
['key3', { title: 'Item 3', value: 300 }],
]),
order: ['key1', 'key2', 'key3']
};
const getItem = (data, index) => {
const key = data.order[index];
return data.items.get(key);
};
const getItemCount = (data) => data.order.length;
const renderItem = ({ item, index }) => (
<View style={styles.customItem}>
<Text>{item.title}: {item.value}</Text>
</View>
);
return (
<VirtualizedList
data={customData}
initialNumToRender={4}
renderItem={renderItem}
keyExtractor={(item, index) => customData.order[index]}
getItemCount={getItemCount}
getItem={getItem}
/>
);
}A component for adding pull-to-refresh functionality to scrollable components.
const RefreshControl: React.ComponentType<RefreshControlProps>;Props:
refreshing - Whether refresh is in progress (required)onRefresh - Callback when refresh is triggeredcolors - Array of colors for refresh indicator (Android)tintColor - Color of refresh indicator (iOS)title - Title text during refresh (iOS)titleColor - Title text color (iOS)progressBackgroundColor - Progress background color (Android)progressViewOffset - Progress view offset (Android)size - Size of refresh indicator (Android)enabled - Whether refresh is enabledUsage:
import { ScrollView, RefreshControl, FlatList } from "react-native-web";
// With ScrollView
function RefreshableScrollView() {
const [refreshing, setRefreshing] = useState(false);
const onRefresh = React.useCallback(async () => {
setRefreshing(true);
try {
await fetchNewData();
} finally {
setRefreshing(false);
}
}, []);
return (
<ScrollView
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
tintColor="#007AFF"
title="Pull to refresh"
titleColor="#007AFF"
colors={['#007AFF', '#4CD964']}
/>
}
>
{/* Content */}
</ScrollView>
);
}
// With FlatList
function RefreshableList() {
const [data, setData] = useState([]);
const [refreshing, setRefreshing] = useState(false);
const loadData = async () => {
const newData = await api.fetchItems();
setData(newData);
};
const onRefresh = async () => {
setRefreshing(true);
await loadData();
setRefreshing(false);
};
return (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={item => item.id}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
colors={['#ff0000', '#00ff00', '#0000ff']}
progressBackgroundColor="#ffffff"
/>
}
/>
);
}// 1. Use getItemLayout for fixed-size items
const getItemLayout = (data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
});
// 2. Optimize renderItem with React.memo
const ListItem = React.memo(({ item, onPress }) => (
<TouchableOpacity onPress={() => onPress(item.id)}>
<Text>{item.title}</Text>
</TouchableOpacity>
));
// 3. Use keyExtractor effectively
const keyExtractor = (item) => item.id.toString();
// 4. Configure window size and batch rendering
<FlatList
windowSize={10} // Items to keep in memory
initialNumToRender={10} // Items in initial batch
maxToRenderPerBatch={5} // Items per batch
removeClippedSubviews={true} // Remove off-screen views
/>
// 5. Avoid inline functions and objects
const styles = StyleSheet.create({ /* ... */ });
const renderItem = useCallback(({ item }) => (
<ListItem item={item} style={styles.item} />
), []);interface FlatListProps<ItemT> extends VirtualizedListProps {
data: ItemT[] | null | undefined;
renderItem: (info: {item: ItemT, index: number, separators: Separators}) => React.ReactElement | null;
keyExtractor?: (item: ItemT, index: number) => string;
// Layout
horizontal?: boolean;
numColumns?: number;
columnWrapperStyle?: ViewStyle;
getItemLayout?: (data: ItemT[] | null | undefined, index: number) => {length: number, offset: number, index: number};
// Performance
initialNumToRender?: number;
maxToRenderPerBatch?: number;
windowSize?: number;
removeClippedSubviews?: boolean;
// Components
ListHeaderComponent?: React.ComponentType | React.ReactElement;
ListFooterComponent?: React.ComponentType | React.ReactElement;
ListEmptyComponent?: React.ComponentType | React.ReactElement;
ItemSeparatorComponent?: React.ComponentType;
// Viewability
onViewableItemsChanged?: (info: {viewableItems: ViewToken[], changed: ViewToken[]}) => void;
viewabilityConfig?: ViewabilityConfig;
// Other
extraData?: any;
inverted?: boolean;
refreshControl?: React.ReactElement;
}
interface SectionListProps<ItemT> extends VirtualizedListProps {
sections: SectionListData<ItemT>[];
renderItem: (info: {item: ItemT, index: number, section: SectionListData<ItemT>, separators: Separators}) => React.ReactElement | null;
renderSectionHeader?: (info: {section: SectionListData<ItemT>}) => React.ReactElement | null;
renderSectionFooter?: (info: {section: SectionListData<ItemT>}) => React.ReactElement | null;
keyExtractor?: (item: ItemT, index: number) => string;
stickySectionHeadersEnabled?: boolean;
SectionSeparatorComponent?: React.ComponentType;
}
interface SectionListData<ItemT> {
data: ItemT[];
key?: string;
title?: string;
[key: string]: any;
}
interface RefreshControlProps extends ViewProps {
refreshing: boolean;
onRefresh?: () => void;
colors?: string[];
enabled?: boolean;
progressBackgroundColor?: ColorValue;
progressViewOffset?: number;
size?: 0 | 1;
tintColor?: ColorValue;
title?: string;
titleColor?: ColorValue;
}
interface ViewToken {
item: any;
key: string;
index: number | null;
isViewable: boolean;
section?: any;
}
interface ViewabilityConfig {
minimumViewTime?: number;
viewAreaCoveragePercentThreshold?: number;
itemVisiblePercentThreshold?: number;
waitForInteraction?: boolean;
}Install with Tessl CLI
npx tessl i tessl/npm-react-native-web