React Native Lists and ScrollView
Almost every app shows a list of items — messages, contacts, products, or news articles. React Native offers two components for scrollable content: ScrollView for short content and FlatList for long or dynamic lists. Choosing the right one affects your app's speed significantly.
ScrollView — Simple Scrollable Container
ScrollView renders all its children at once. The entire content loads into memory before the user sees anything. This works well for short forms, settings screens, or pages with fewer than 20 items.
import { ScrollView, Text, View } from 'react-native';
export default function SettingsScreen() {
return (
<ScrollView contentContainerStyle={{ padding: 16 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold' }}>Account</Text>
<Text>Profile Settings</Text>
<Text>Privacy</Text>
<Text>Security</Text>
<Text style={{ fontSize: 18, fontWeight: 'bold', marginTop: 20 }}>App</Text>
<Text>Notifications</Text>
<Text>Display</Text>
<Text>Language</Text>
</ScrollView>
);
}
ScrollView Direction
Vertical scroll (default): Horizontal scroll:
┌─────────┐ ┌─────────────────────────────┐
│ Item 1 │ ▲ │ Card 1 │ Card 2 │ Card 3 ──►
│ Item 2 │ └─────────────────────────────┘
│ Item 3 │
│ Item 4 │ ▼
└─────────┘
Horizontal: <ScrollView horizontal={true}>...</ScrollView>
FlatList — Efficient Long Lists
FlatList only renders the items visible on screen plus a small buffer above and below. Items that scroll off screen are removed from memory. Items about to scroll into view are added. This lazy approach handles thousands of items without slowing down.
ScrollView FlatList ──────────────────────────────────────────────────────── Renders: ALL items at once Renders: only visible items ┌──────────┐ ← Item 1 in RAM ┌──────────┐ ← Item 3 in RAM │ Item 1 │ │ Item 3 │ │ Item 2 │ │ Item 4 │ │ Item 3 │ ← visible area │ Item 5 │ ← visible area │ Item 4 │ │ Item 6 │ │ Item 5 │ ← Item 200 in RAM └──────────┘ ← Items 1,2,200 recycled │ ...200 │ └──────────┘
Basic FlatList
import { FlatList, Text, View, StyleSheet } from 'react-native';
const contacts = [
{ id: '1', name: 'Alice', phone: '555-0101' },
{ id: '2', name: 'Bob', phone: '555-0102' },
{ id: '3', name: 'Carol', phone: '555-0103' },
];
function ContactItem({ name, phone }) {
return (
<View style={styles.item}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.phone}>{phone}</Text>
</View>
);
}
export default function ContactList() {
return (
<FlatList
data={contacts}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <ContactItem name={item.name} phone={item.phone} />}
/>
);
}
Screen: ┌──────────────────────────────┐ │ Alice │ │ 555-0101 │ ├──────────────────────────────┤ │ Bob │ │ 555-0102 │ ├──────────────────────────────┤ │ Carol │ │ 555-0103 │ └──────────────────────────────┘
Three Required FlatList Props
Prop Purpose ──────────────────────────────────────────────────────── data The array of items to display keyExtractor Returns a unique string ID for each item renderItem A function that returns the JSX for one item
Adding a Separator Between Items
<FlatList
data={contacts}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <ContactItem {...item} />}
ItemSeparatorComponent={() => (
<View style={{ height: 1, backgroundColor: '#E5E5EA', marginLeft: 16 }} />
)}
/>
Adding a Header and Footer
<FlatList
data={contacts}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <ContactItem {...item} />}
ListHeaderComponent={
<Text style={{ padding: 16, fontWeight: 'bold', fontSize: 20 }}>
Contacts
</Text>
}
ListFooterComponent={
<Text style={{ padding: 16, color: '#999', textAlign: 'center' }}>
{contacts.length} contacts total
</Text>
}
/>
Empty State
Show a helpful message when the list has no data to display.
<FlatList
data={filteredContacts}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <ContactItem {...item} />}
ListEmptyComponent={
<View style={{ alignItems: 'center', marginTop: 60 }}>
<Text style={{ fontSize: 48 }}>🔍</Text>
<Text style={{ color: '#999', marginTop: 8 }}>No contacts found</Text>
</View>
}
/>
Pull-to-Refresh
Users expect to pull the list down to refresh the content. FlatList supports this with two props.
const [refreshing, setRefreshing] = useState(false);
async function handleRefresh() {
setRefreshing(true);
await fetchContacts(); // re-fetch your data
setRefreshing(false);
}
<FlatList
data={contacts}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <ContactItem {...item} />}
refreshing={refreshing}
onRefresh={handleRefresh}
/>
SectionList — Grouped Lists
SectionList organises items into labelled sections — like a contacts list grouped by first letter.
import { SectionList } from 'react-native';
const sections = [
{ title: 'A', data: ['Alice', 'Andrew'] },
{ title: 'B', data: ['Bob', 'Brenda'] },
{ title: 'C', data: ['Carol'] },
];
<SectionList
sections={sections}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => <Text style={{ padding: 12 }}>{item}</Text>}
renderSectionHeader={({ section: { title } }) => (
<Text style={{ backgroundColor: '#f0f0f0', padding: 8, fontWeight: 'bold' }}>
{title}
</Text>
)}
/>
Screen:
┌──────────────────────┐
│ A │ ← section header
│ Alice │
│ Andrew │
│ B │ ← section header
│ Bob │
│ Brenda │
└──────────────────────┘
Choosing the Right Component
Situation Best Component ────────────────────────────────────────────────── Short form or settings screen ScrollView Long or dynamic list FlatList Grouped list (A–Z, categories) SectionList Horizontal card carousel FlatList + horizontal
Summary
Use ScrollView for short content that fits within a few screens. Use FlatList for any list that could grow or already contains many items — it only renders what is visible. SectionList adds section headers for grouped data. Pull-to-refresh, empty states, and separators make your list experience feel polished and professional.
