Skip to main content

List

The List component provides a beautiful container for vertical lists with automatic borders, padding, and virtualization support.

Import

import { List } from 'papillon-ui';

Basic Usage

import { List, Item, Typography } from 'papillon-ui';

<List>
  <Item onPress={() => {}}>
    <Typography variant="title">First Item</Typography>
  </Item>
  <Item onPress={() => {}}>
    <Typography variant="title">Second Item</Typography>
  </Item>
  <Item onPress={() => {}}>
    <Typography variant="title">Third Item</Typography>
  </Item>
</List>

Props

data
any[]
Array of data items to render. When provided, enables automatic virtualization for large lists.
children
React.ReactNode
Child elements to render inside the list. Use this for static lists.
disablePadding
boolean
default:false
Removes automatic padding from non-Item children.
ignoreBorder
boolean
default:false
Suppresses the bottom border for all items in the list.
animated
boolean
default:true
Enables layout transition animations.
radius
number
default:20
Border radius of the list container.
marginBottom
number
default:12
Bottom margin of the list container.
contentContainerStyle
StyleProp<ViewStyle>
Style applied to the content container of each list item.
entering
EntryExitTransition
Custom Reanimated entering animation.
exiting
EntryExitTransition
Custom Reanimated exiting animation.
disableItemAnimation
boolean
default:false
Disables item layout animations for better performance.

With Data Array

For large lists, use the data prop to enable automatic virtualization:
const items = [
  { id: '1', title: 'Grades', icon: 'star' },
  { id: '2', title: 'Homework', icon: 'book' },
  { id: '3', title: 'Timetable', icon: 'calendar' },
];

<List data={items}>
  {items.map(item => (
    <Item key={item.id} onPress={() => {}}>
      <Typography variant="title">{item.title}</Typography>
    </Item>
  ))}
</List>
When the data array contains 20+ items, List automatically uses @legendapp/list for virtualization.

Automatic Borders

List automatically adds borders between items:
<List>
  <Item><Typography>Item 1</Typography></Item>
  <Item><Typography>Item 2</Typography></Item> {/* Border above */}
  <Item><Typography>Item 3</Typography></Item> {/* Border above */}
</List>

Disable Borders

<List ignoreBorder>
  <Item><Typography>No borders</Typography></Item>
  <Item><Typography>Between items</Typography></Item>
</List>

Automatic Padding

Non-Item children automatically receive padding:
<List>
  <Typography variant="title">Auto-padded title</Typography>
  <Typography variant="caption">Auto-padded caption</Typography>
</List>

Disable Padding

<List disablePadding>
  <View style={{ padding: 20 }}>
    <Typography>Custom padding</Typography>
  </View>
</List>

Mixed Content

Combine Item components with custom children:
<List>
  <View style={{ padding: 16 }}>
    <Typography variant="h5">Section Header</Typography>
  </View>
  
  <Item><Typography>Item 1</Typography></Item>
  <Item><Typography>Item 2</Typography></Item>
  
  <View style={{ padding: 16 }}>
    <Typography variant="caption" color="secondary">
      2 items
    </Typography>
  </View>
</List>

Styling

Custom Border Radius

<List radius={12}>
  <Item><Typography>Smaller radius</Typography></Item>
</List>

<List radius={30}>
  <Item><Typography>Larger radius</Typography></Item>
</List>

Custom Spacing

<List marginBottom={24}>
  <Item><Typography>More bottom margin</Typography></Item>
</List>

Content Container Style

Apply styles to all list item containers:
<List contentContainerStyle={{ paddingHorizontal: 20 }}>
  <Item><Typography>Extra horizontal padding</Typography></Item>
</List>

Animations

Entry/Exit Animations

import { FadeIn, FadeOut } from 'react-native-reanimated';

<List 
  entering={FadeIn.duration(300)}
  exiting={FadeOut.duration(200)}
>
  <Item><Typography>Animated list</Typography></Item>
</List>

Disable Animations

For performance optimization:
<List animated={false} disableItemAnimation>
  {longArray.map(item => (
    <Item key={item.id}>
      <Typography>{item.name}</Typography>
    </Item>
  ))}
</List>

Performance Optimization

Virtualization Threshold

List automatically virtualizes when data has 20+ items:
const manyItems = Array.from({ length: 100 }, (_, i) => ({ 
  id: i, 
  name: `Item ${i}` 
}));

<List data={manyItems}> {/* Automatically virtualized */}
  {manyItems.map(item => (
    <Item key={item.id}>
      <Typography>{item.name}</Typography>
    </Item>
  ))}
</List>

Memoization

List uses aggressive memoization:
  • Shallow comparison of data and children props
  • Cached style calculations
  • Pre-computed border and padding styles

Disable Animations for Long Lists

<List 
  data={longArray}
  disableItemAnimation // Better performance
  animated={false}
>
  {/* Items */}
</List>

Theming

List automatically uses your React Navigation theme:
import { useTheme } from '@react-navigation/native';

function ThemedList() {
  const { colors } = useTheme();
  
  // List automatically uses:
  // - colors.card for background
  // - colors.border for border
  // - colors.text + opacity for item separators
  
  return (
    <List>
      <Item><Typography>Themed list</Typography></Item>
    </List>
  );
}

Accessibility

<List
  accessible
  accessibilityRole="list"
  accessibilityLabel="Settings menu"
>
  <Item accessibilityRole="button">
    <Typography>Setting 1</Typography>
  </Item>
</List>

Best Practices

When rendering lists from API data or state, use the data prop to enable automatic virtualization:
const { data: grades } = useGrades();

<List data={grades}>
  {grades.map(grade => (
    <GradeItem key={grade.id} grade={grade} />
  ))}
</List>
For optimal performance, wrap list items in React.memo:
const MemoizedItem = React.memo(({ item }) => (
  <Item onPress={() => {}}>
    <Typography>{item.title}</Typography>
  </Item>
));

<List data={items}>
  {items.map(item => <MemoizedItem key={item.id} item={item} />)}
</List>
Always use the Item component for pressable list rows to get proper animations and styling:
<List>
  <Item onPress={() => {}}> {/* Good */}
    <Typography>Pressable</Typography>
  </Item>
</List>

Full Example

import { List, Item, Typography } from 'papillon-ui';
import { ChevronRight, User, Settings, Bell } from 'lucide-react-native';

function SettingsList() {
  const settings = [
    { id: '1', title: 'Profile', icon: User, route: '/profile' },
    { id: '2', title: 'Notifications', icon: Bell, route: '/notifications' },
    { id: '3', title: 'Settings', icon: Settings, route: '/settings' },
  ];
  
  return (
    <List data={settings} radius={20}>
      {settings.map(setting => (
        <Item key={setting.id} onPress={() => navigate(setting.route)}>
          <Item.Leading>
            <setting.icon size={24} />
          </Item.Leading>
          
          <Typography variant="title">{setting.title}</Typography>
          
          <Item.Trailing>
            <ChevronRight size={20} />
          </Item.Trailing>
        </Item>
      ))}
    </List>
  );
}

Build docs developers (and LLMs) love