CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-native-web

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.

Pending
Overview
Eval results
Files

list-components.mddocs/

List Components

High-performance list and virtualization components for handling large datasets efficiently with comprehensive scroll management, pull-to-refresh, and advanced rendering optimizations.

FlatList

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 render
  • renderItem - Function to render each item: ({item, index, separators}) => React.Element

Core Props:

  • keyExtractor - Extract unique key for each item: (item, index) => string
  • extraData - Additional data to trigger re-renders when changed
  • horizontal - Render items horizontally instead of vertically
  • numColumns - Number of columns for grid layout (vertical only)
  • initialNumToRender - Items to render in initial batch
  • initialScrollIndex - Start at specific index (requires getItemLayout)
  • inverted - Reverse scroll direction
  • removeClippedSubviews - Optimize performance by removing off-screen views

Layout Optimization:

  • getItemLayout - Provide item dimensions for performance: (data, index) => {length, offset, index}
  • columnWrapperStyle - Style for multi-column rows
  • ListHeaderComponent - Component at the top of the list
  • ListFooterComponent - Component at the bottom of the list
  • ListEmptyComponent - Component when list is empty
  • ItemSeparatorComponent - Component between items

Scroll Props:

  • onScroll - Scroll event handler
  • onScrollToIndexFailed - Called when scrollToIndex fails
  • scrollEventThrottle - Scroll event throttling
  • showsHorizontalScrollIndicator - Show horizontal scroll bar
  • showsVerticalScrollIndicator - Show vertical scroll bar

Viewability:

  • onViewableItemsChanged - Called when viewable items change
  • viewabilityConfig - Configuration for viewability callbacks
  • viewabilityConfigCallbackPairs - Multiple viewability configs

Methods:

  • scrollToEnd({animated?}) - Scroll to end of list
  • scrollToIndex({index, animated?, viewPosition?, viewOffset?}) - Scroll to specific index
  • scrollToItem({item, animated?, viewPosition?, viewOffset?}) - Scroll to specific item
  • scrollToOffset({offset, animated?}) - Scroll to pixel offset
  • recordInteraction() - Trigger viewability calculations
  • flashScrollIndicators() - Show scroll indicators briefly

Usage:

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}
    />
  );
}

SectionList

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 property
  • renderItem - Function to render each item
  • renderSectionHeader - Function to render section headers

Section Object:

{
  title: string,
  data: Array<ItemT>,
  key?: string, // Optional, will use index if not provided
  // Any additional custom properties
}

Props:

  • keyExtractor - Extract key for items
  • renderSectionFooter - Render section footers
  • SectionSeparatorComponent - Component between sections
  • ItemSeparatorComponent - Component between items
  • stickySectionHeadersEnabled - Make headers stick to top
  • onScrollToIndexFailed - Handle scroll failures

Usage:

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}
    />
  );
}

VirtualizedList

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) => ItemT
  • getItemCount - Function to get total count: (data) => number
  • renderItem - Render function for items

Advanced Props:

  • CellRendererComponent - Custom cell renderer
  • ListHeaderComponent - Header component
  • ListFooterComponent - Footer component
  • debug - Enable debug mode
  • disableVirtualization - Disable virtualization for debugging

Usage:

// 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}
    />
  );
}

RefreshControl

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 triggered
  • colors - 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 enabled

Usage:

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"
        />
      }
    />
  );
}

Performance Optimization Tips

// 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} />
), []);

Types

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

docs

accessibility.md

animation.md

core-utilities.md

form-controls.md

hooks.md

index.md

interactive-components.md

layout-components.md

list-components.md

media-components.md

platform-apis.md

stylesheet.md

system-integration.md

text-input.md

tile.json