Mamun Labs Learn
JourneyGlossary
esc
  • Try “map”, “flexbox” or “state”.
Log in
Start learning
Phase 6 · 5 / 10Lists with FlatList
Phase 6React Native with Expo
10 topics · 6–8 wks
  1. 1Your first Expo app
  2. 2View, Text and Image
  3. 3Pressable and TextInput
  4. 4Styling and flexbox on mobile
  5. 5Lists with FlatList
  6. 6Safe areas and the keyboard
  7. 7Screens and navigation
  8. 8Tabs
  9. 9Saving data on the phone
  10. 10Calling an API
JourneyPhase 6Lists with FlatList
01 / 10Overview

Lists with FlatList

You'll learn to show a long, scrolling list with FlatList, and to read its three main props.

Why it mattersThe habits list is the heart of your app, and almost every app has a list like it, from chats to contacts.
about 90 min·needs: Styling and flexbox on mobile, Lists and keys
02 / 10

Watch & read

Start with the required ones. The others are there when you need them.

Docs
Using list viewsreactnative.dev · official docs · 10 min · English
MMamun's noteA short page. Read the FlatList part and run the example: it shows data and renderItem with ({ item }). Skip SectionList, which groups rows under headings; you won't need it for the habit tracker.
Video
Complete React Native tutorial #25: using the FlatList componentYouTube · Net Ninja · 7 min · English
MMamun's noteWatch all seven minutes. He passes data, keyExtractor and renderItem one at a time and explains each, then styles the rows. His books come from a database; for you, the array is your habits.
DocsOptional
FlatListreactnative.dev · official docs · 15 min · English
MMamun's noteThe reference page. Read the first example, then look up keyExtractor, ListEmptyComponent and ItemSeparatorComponent in the props list. You don't need the rest yet.
03 / 10

See it step by step

FlatList renders a window of rows around the screen, not the whole list.

data length1000
rows on screen"none yet"
rows rendered0
App.jsx
1const habits = makeHabits(1000); // 1,000 habits, ids h1 to h1000
2
3export default function App() {
4 return (
5 <FlatList
6 data={habits}
7 keyExtractor={(habit) => habit.id}
8 renderItem={({ item }) => <HabitRow habit={item} />}
9 />
10 );
11}
You have 1,000 habits in one array. On this phone, about 8 rows fit on the screen at once.
Step 1 / 6
04 / 10

Explanation

You want to show every habit as a row, and the list must scroll when it gets longer than the screen. In React on the web you used map. On a phone, a plain View does not scroll at all. You need a list component.

Think of your phone's contacts app with 800 names. It does not draw all 800 at once. It draws the ones on screen, plus a few above and below, and draws more as you scroll. FlatList works the same way. It renders the rows near the screen and removes ones far away. That keeps long lists fast and saves memory.

<FlatList
data={habits}
keyExtractor={(habit) => habit.id}
renderItem={({ item }) => <Text>{item.name}</Text>}
/>
  • data is your array.
  • renderItem is a function that draws one row. FlatList calls it with an object, and your habit is inside it as item. That is why you see ({ item }) with curly brackets. You also get index.
  • keyExtractor returns a unique string for each row, like the key prop you used with map. If you leave it out, FlatList looks for item.key, then item.id, then uses the position.

A ScrollView with habits.map(...) inside also scrolls. It is fine for a short list that never grows, like a settings page. But it renders every row at once, so use FlatList for anything that can get long.

People often write renderItem={(habit) => <Text>{habit.name}</Text>}. The argument is not the habit, it is a wrapper { item, index }. habit.name is undefined, so every row is empty. Unpack it with ({ item }).

Two more props you'll meet: ListEmptyComponent shows something when data is empty ("No habits yet"), and ItemSeparatorComponent draws a line between rows.

When you read a FlatList, find data first (what list?), then renderItem (what does one row look like?), then keyExtractor (what makes each row unique?).

Words to recognise
FlatListScrollViewdatarenderItemkeyExtractor
05 / 10

Code tour

Click a line number with a ring to read the note for that line.

src/app/index.jsx5 notes
1import { FlatList, Text } from "react-native";
2const habits = [
{ id: "h1", name: "Drink water", done: true },
Mentor note · line 3
Each habit has its own id. Ids from your data make the best keys, because they never change when the list is sorted.
4 { id: "h2", name: "Read 10 pages", done: false },
5];
6export default function HomeScreen() {
7 return (
8 <FlatList
data={habits}
keyExtractor={(habit) => habit.id}
renderItem={({ item }) => <Text>{item.done ? "✓ " : "○ "}{item.name}</Text>}
ListEmptyComponent={<Text>No habits yet. Add one!</Text>}
13 />
14 );
15}
06 / 10

Practice

Short drills. Take your time and read the code before you answer.

DrillPredict1 / 2
1const habits = [
2 { id: "h1", name: "Drink water" },
3 { id: "h2", name: "Read 10 pages" },
4 { id: "h3", name: "Walk 20 minutes" },
5];
6
7export default function App() {
8 return (
9 <FlatList
10 data={habits}
11 renderItem={(habit) => <Text>{habit.name}</Text>}
12 />
13 );
14}
What appears on screen?
DrillTick a habit2 / 2
Your habits show in a FlatList. Make a tap on a row tick it: write toggle so it gives back a new array where only the tapped habit has done flipped. Then check the empty list message.
1import { useState } from "react";
2import { FlatList, Pressable, StyleSheet, Text, View } from "react-native";
3
4const start = [
5 { id: "h1", name: "Drink water", done: false },
6 { id: "h2", name: "Read 10 pages", done: false },

Runs in the browser. Choose “My Device” in Snack to try it on your phone with Expo Go.

Working whenTapping a habit puts a tick next to it, and tapping it again takes the tick away.Ticking one habit leaves the others as they were.If you change useState(start) to useState([]), the screen says "No habits yet."
Check each goal yourself, then mark it.
07 / 10

Quick check

3 questions. Each one explains itself after you answer.

  1. 1 / 3In renderItem={({ item }) => ...}, what is item?
  2. 2 / 3Why use FlatList instead of habits.map inside a ScrollView for a long list?
  3. 3 / 3Your habits look like { id: "h1", name: "Walk" }. What should keyExtractor be?
08 / 10

Explain it back

Only you can see this. Log in to keep it.0 words
09 / 10

How confident are you with Lists with FlatList?

10 / 10

Done

You can now
Read a FlatList and say what each row shows and which value is its key.
Next: Safe areas and the keyboard Back to Phase 6
On this page
  1. 01Lists with FlatList
  2. 02Watch & read
  3. 03See it step by step
  4. 04Explanation
  5. 05Code tour
  6. 06Practice
  7. 07Quick check
  8. 08Explain it back
  9. 09Confidence
  10. 10Done
Up next · 02 / 10Watch & read
Mamun Labs LearnFound a mistake on this page? Tell Mamun.
Mamun Labs Learn
JourneyGlossary
esc
  • Try “map”, “flexbox” or “state”.
Log in
Start learning
Log in to save your progress. Everything here works without an account.Log in to save progressLog in
Phase 6React Native with Expo
10 topics · 6–8 wks
  1. 1Your first Expo app
  2. 2View, Text and Image
  3. 3Pressable and TextInput
  4. 4Styling and flexbox on mobile
  5. 5Lists with FlatList
  6. 6Safe areas and the keyboard
  7. 7Screens and navigation
  8. 8Tabs
  9. 9Saving data on the phone
  10. 10Calling an API
JourneyPhase 6Lists with FlatList
01 / 10Overview

Lists with FlatList

You'll learn to show a long, scrolling list with FlatList, and to read its three main props.

Why it mattersThe habits list is the heart of your app, and almost every app has a list like it, from chats to contacts.
about 90 min·needs: Styling and flexbox on mobile, Lists and keys
02 / 10

Watch & read

Start with the required ones. The others are there when you need them.

Docs
Using list viewsreactnative.dev · official docs · 10 min · English
MMamun's noteA short page. Read the FlatList part and run the example: it shows data and renderItem with ({ item }). Skip SectionList, which groups rows under headings; you won't need it for the habit tracker.
Video
Complete React Native tutorial #25: using the FlatList componentYouTube · Net Ninja · 7 min · English
MMamun's noteWatch all seven minutes. He passes data, keyExtractor and renderItem one at a time and explains each, then styles the rows. His books come from a database; for you, the array is your habits.
DocsOptional
FlatListreactnative.dev · official docs · 15 min · English
MMamun's noteThe reference page. Read the first example, then look up keyExtractor, ListEmptyComponent and ItemSeparatorComponent in the props list. You don't need the rest yet.
03 / 10

See it step by step

FlatList renders a window of rows around the screen, not the whole list.

data length1000
rows on screen"none yet"
rows rendered0
App.jsx
1const habits = makeHabits(1000); // 1,000 habits, ids h1 to h1000
2
3export default function App() {
4 return (
5 <FlatList
6 data={habits}
7 keyExtractor={(habit) => habit.id}
8 renderItem={({ item }) => <HabitRow habit={item} />}
9 />
10 );
11}
You have 1,000 habits in one array. On this phone, about 8 rows fit on the screen at once.
Step 1 / 6
04 / 10

Explanation

You want to show every habit as a row, and the list must scroll when it gets longer than the screen. In React on the web you used map. On a phone, a plain View does not scroll at all. You need a list component.

Think of your phone's contacts app with 800 names. It does not draw all 800 at once. It draws the ones on screen, plus a few above and below, and draws more as you scroll. FlatList works the same way. It renders the rows near the screen and removes ones far away. That keeps long lists fast and saves memory.

<FlatList
data={habits}
keyExtractor={(habit) => habit.id}
renderItem={({ item }) => <Text>{item.name}</Text>}
/>
  • data is your array.
  • renderItem is a function that draws one row. FlatList calls it with an object, and your habit is inside it as item. That is why you see ({ item }) with curly brackets. You also get index.
  • keyExtractor returns a unique string for each row, like the key prop you used with map. If you leave it out, FlatList looks for item.key, then item.id, then uses the position.

A ScrollView with habits.map(...) inside also scrolls. It is fine for a short list that never grows, like a settings page. But it renders every row at once, so use FlatList for anything that can get long.

People often write renderItem={(habit) => <Text>{habit.name}</Text>}. The argument is not the habit, it is a wrapper { item, index }. habit.name is undefined, so every row is empty. Unpack it with ({ item }).

Two more props you'll meet: ListEmptyComponent shows something when data is empty ("No habits yet"), and ItemSeparatorComponent draws a line between rows.

When you read a FlatList, find data first (what list?), then renderItem (what does one row look like?), then keyExtractor (what makes each row unique?).

Words to recognise
FlatListScrollViewdatarenderItemkeyExtractor
05 / 10

Code tour

Click a line number with a ring to read the note for that line.

src/app/index.jsx5 notes
1import { FlatList, Text } from "react-native";
2const habits = [
{ id: "h1", name: "Drink water", done: true },
Mentor note · line 3
Each habit has its own id. Ids from your data make the best keys, because they never change when the list is sorted.
4 { id: "h2", name: "Read 10 pages", done: false },
5];
6export default function HomeScreen() {
7 return (
8 <FlatList
data={habits}
keyExtractor={(habit) => habit.id}
renderItem={({ item }) => <Text>{item.done ? "✓ " : "○ "}{item.name}</Text>}
ListEmptyComponent={<Text>No habits yet. Add one!</Text>}
13 />
14 );
15}
06 / 10

Practice

Short drills. Take your time and read the code before you answer.

DrillPredict1 / 2
1const habits = [
2 { id: "h1", name: "Drink water" },
3 { id: "h2", name: "Read 10 pages" },
4 { id: "h3", name: "Walk 20 minutes" },
5];
6
7export default function App() {
8 return (
9 <FlatList
10 data={habits}
11 renderItem={(habit) => <Text>{habit.name}</Text>}
12 />
13 );
14}
What appears on screen?
DrillTick a habit2 / 2
Your habits show in a FlatList. Make a tap on a row tick it: write toggle so it gives back a new array where only the tapped habit has done flipped. Then check the empty list message.
1import { useState } from "react";
2import { FlatList, Pressable, StyleSheet, Text, View } from "react-native";
3
4const start = [
5 { id: "h1", name: "Drink water", done: false },
6 { id: "h2", name: "Read 10 pages", done: false },

Runs in the browser. Choose “My Device” in Snack to try it on your phone with Expo Go.

Working whenTapping a habit puts a tick next to it, and tapping it again takes the tick away.Ticking one habit leaves the others as they were.If you change useState(start) to useState([]), the screen says "No habits yet."
Check each goal yourself, then mark it.
07 / 10

Quick check

3 questions. Each one explains itself after you answer.

  1. 1 / 3In renderItem={({ item }) => ...}, what is item?
  2. 2 / 3Why use FlatList instead of habits.map inside a ScrollView for a long list?
  3. 3 / 3Your habits look like { id: "h1", name: "Walk" }. What should keyExtractor be?
08 / 10

Explain it back

Only you can see this. Log in to keep it.0 words
09 / 10

How confident are you with Lists with FlatList?

10 / 10

Done

You can now
Read a FlatList and say what each row shows and which value is its key.
Next: Safe areas and the keyboard Back to Phase 6
On this page
  1. 01Lists with FlatList
  2. 02Watch & read
  3. 03See it step by step
  4. 04Explanation
  5. 05Code tour
  6. 06Practice
  7. 07Quick check
  8. 08Explain it back
  9. 09Confidence
  10. 10Done
Up next · 02 / 10Watch & read

You want to show every habit as a row, and the list must scroll when it gets longer than the screen. In React on the web you used map. On a phone, a plain View does not scroll at all. You need a list component.

Think of your phone's contacts app with 800 names. It does not draw all 800 at once. It draws the ones on screen, plus a few above and below, and draws more as you scroll. FlatList works the same way. It renders the rows near the screen and removes ones far away. That keeps long lists fast and saves memory.

<FlatList
data={habits}
keyExtractor={(habit) => habit.id}
renderItem={({ item }) => <Text>{item.name}</Text>}
/>
  • data is your array.
  • renderItem is a function that draws one row. FlatList calls it with an object, and your habit is inside it as item. That is why you see ({ item }) with curly brackets. You also get index.
  • keyExtractor returns a unique string for each row, like the key prop you used with map. If you leave it out, FlatList looks for item.key, then item.id, then uses the position.

A ScrollView with habits.map(...) inside also scrolls. It is fine for a short list that never grows, like a settings page. But it renders every row at once, so use FlatList for anything that can get long.

People often write renderItem={(habit) => <Text>{habit.name}</Text>}. The argument is not the habit, it is a wrapper { item, index }. habit.name is undefined, so every row is empty. Unpack it with ({ item }).

Two more props you'll meet: ListEmptyComponent shows something when data is empty ("No habits yet"), and ItemSeparatorComponent draws a line between rows.

When you read a FlatList, find data first (what list?), then renderItem (what does one row look like?), then keyExtractor (what makes each row unique?).

Words to recognise
FlatListScrollViewdatarenderItemkeyExtractor

You want to show every habit as a row, and the list must scroll when it gets longer than the screen. In React on the web you used map. On a phone, a plain View does not scroll at all. You need a list component.

Think of your phone's contacts app with 800 names. It does not draw all 800 at once. It draws the ones on screen, plus a few above and below, and draws more as you scroll. FlatList works the same way. It renders the rows near the screen and removes ones far away. That keeps long lists fast and saves memory.

<FlatList
data={habits}
keyExtractor={(habit) => habit.id}
renderItem={({ item }) => <Text>{item.name}</Text>}
/>
  • data is your array.
  • renderItem is a function that draws one row. FlatList calls it with an object, and your habit is inside it as item. That is why you see ({ item }) with curly brackets. You also get index.
  • keyExtractor returns a unique string for each row, like the key prop you used with map. If you leave it out, FlatList looks for item.key, then item.id, then uses the position.

A ScrollView with habits.map(...) inside also scrolls. It is fine for a short list that never grows, like a settings page. But it renders every row at once, so use FlatList for anything that can get long.

People often write renderItem={(habit) => <Text>{habit.name}</Text>}. The argument is not the habit, it is a wrapper { item, index }. habit.name is undefined, so every row is empty. Unpack it with ({ item }).

Two more props you'll meet: ListEmptyComponent shows something when data is empty ("No habits yet"), and ItemSeparatorComponent draws a line between rows.

When you read a FlatList, find data first (what list?), then renderItem (what does one row look like?), then keyExtractor (what makes each row unique?).

Words to recognise
FlatListScrollViewdatarenderItemkeyExtractor