Lists with FlatList
A React Native screen does not scroll by itself: if there are more tasks than fit, the bottom ones are simply not visible. Lists use FlatList, a scrolling list that creates only the visible rows on the screen and therefore stays fast even with thousands of items.
The row component
Move the task row into a separate component:
// components/TaskItem.tsx
import { Pressable, StyleSheet, Text } from 'react-native';
export type Task = { id: string; title: string; done: boolean };
type Props = { task: Task; onToggle: (id: string) => void };
export default function TaskItem({ task, onToggle }: Props) {
return (
<Pressable onPress={() => onToggle(task.id)} style={styles.row}>
<Text style={[styles.title, task.done && styles.done]}>{task.title}</Text>
</Pressable>
);
}
const styles = StyleSheet.create({
row: { paddingVertical: 14, paddingHorizontal: 16 },
title: { fontSize: 18 },
done: { color: '#999', textDecorationLine: 'line-through' },
});The Task type is now exported from here, and app/index.tsx can import it instead of declaring its own.
FlatList
Replace tasks.map(...) in app/index.tsx with a FlatList:
// app/index.tsx
import { FlatList } from 'react-native';
import TaskItem, { type Task } from '@/components/TaskItem';
<FlatList
data={tasks}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <TaskItem task={item} onToggle={toggleTask} />}
/>Add twenty tasks and the list starts scrolling with your finger. The three main props:
| Prop | What to pass |
|---|---|
data | An array of items |
renderItem | A function that receives item and index and returns a row |
keyExtractor | A function returning a unique string key |
renderItem receives an object rather than the item itself, so the parameter is destructured: ({ item }).
Separators, header and an empty list
// app/index.tsx
<FlatList
data={tasks}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <TaskItem task={item} onToggle={toggleTask} />}
ItemSeparatorComponent={() => <View style={{ height: 1, backgroundColor: '#eee' }} />}
ListEmptyComponent={<Text style={{ padding: 16 }}>No tasks yet</Text>}
ListHeaderComponent={<Text style={{ fontSize: 24, padding: 16 }}>My tasks</Text>}
/>Thin grey lines appear between rows, and a heading at the top scrolls together with the list. While there are no tasks, "No tasks yet" is shown instead of the list.
Where to place a FlatList
A FlatList must have a limited height, otherwise it does not know what to scroll. It is usually placed in a root View with flex: 1, and the list takes the space left under the input field and the button.
Common mistake
Forgetting keyExtractor when the items have no key field. React Native shows a warning about keys, and rows may re-render incorrectly when tasks are deleted. If the data has an id field, always pass keyExtractor={(item) => item.id}; the key must be a string, so numbers are converted with String(item.id).