FlatList is one of the most important React Native components for displaying lists of data. When a REST API returns many records, FlatList can display those records efficiently without rendering every item at the same time.
FlatList is a React Native component designed to display a list of data.
<FlatList
data={students}
renderItem={({ item }) => (
<Text>
{item.name}
</Text>
)}
/>
It is especially useful when the application has many records.
import {
FlatList
} from "react-native";
FlatList is included with React Native, so no external package is required.
<FlatList
data={students}
renderItem={({ item }) => (
<Text>
{item.name}
</Text>
)}
/>
The two most important properties are data and renderItem.
The data property contains the array that FlatList should display.
const students = [
{
id: 1,
name: "Rahul"
},
{
id: 2,
name: "Amit"
}
];
<FlatList
data={students}
/>
renderItem tells FlatList how each record should be displayed.
<FlatList
data={students}
renderItem={({ item }) => (
<Text>
{item.name}
</Text>
)}
/>
The item variable represents the current record being rendered.
renderItem={({ item }) => (
<Text>
{item.name}
</Text>
)}
If the current record is a student, item contains that student's data.
<FlatList
data={students}
renderItem={({ item }) => (
<View>
<Text>
Name: {item.name}
</Text>
<Text>
Email: {item.email}
</Text>
<Text>
Course: {item.course}
</Text>
</View>
)}
/>
keyExtractor gives each FlatList item a stable key.
<FlatList
data={students}
keyExtractor={item =>
item.id.toString()
}
renderItem={({ item }) => (
<Text>
{item.name}
</Text>
)}
/>
const [students, setStudents] =
useState([]);
async function loadStudents() {
const response =
await fetch(API_URL);
const result =
await response.json();
setStudents(
result.data || []
);
}
The returned array can then be passed to FlatList.
useEffect(() => {
loadStudents();
}, []);
This loads the API data when the screen is initially rendered.
if (loading) {
return (
<ActivityIndicator
size="large"
/>
);
}
return (
<FlatList
data={students}
renderItem={renderStudent}
/>
);
if (error) {
return (
<View>
<Text>
{error}
</Text>
</View>
);
}
return (
<FlatList
data={students}
renderItem={renderStudent}
/>
);
ListEmptyComponent displays content when the data array is empty.
<FlatList
data={students}
renderItem={renderStudent}
ListEmptyComponent={
<Text>
No students found.
</Text>
}
/>
ItemSeparatorComponent can display a separator between list items.
function Separator() {
return (
<View
style={{
height: 1
}}
/>
);
}
<FlatList
data={students}
renderItem={renderStudent}
ItemSeparatorComponent={
Separator
}
/>
ListHeaderComponent displays content above the list.
<FlatList
data={students}
renderItem={renderStudent}
ListHeaderComponent={
<Text>
Student List
</Text>
}
/>
ListFooterComponent displays content below the list.
<FlatList
data={students}
renderItem={renderStudent}
ListFooterComponent={
<Text>
End of Student List
</Text>
}
/>
FlatList supports pull-to-refresh using the refreshing and onRefresh properties.
<FlatList
data={students}
renderItem={renderStudent}
refreshing={loading}
onRefresh={loadStudents}
/>
const [refreshing, setRefreshing] =
useState(false);
async function refreshStudents() {
setRefreshing(true);
try {
await loadStudents();
} finally {
setRefreshing(false);
}
}
This separates refresh activity from other loading operations when needed.
function renderStudent({
item
}) {
return (
<View>
<Text>
{item.name}
</Text>
<Text>
{item.email}
</Text>
<Text>
{item.course}
</Text>
</View>
);
}
Keeping the item rendering logic in a separate function can make the FlatList easier to read.
TypeScript can describe the structure of each API record.
interface Student {
id: number;
name: string;
email: string;
course: string;
}
const [students, setStudents] =
useState<Student[]>([]);
const renderStudent = ({
item
}: {
item: Student
}) => {
return (
<View>
<Text>
{item.name}
</Text>
<Text>
{item.email}
</Text>
</View>
);
};
This helps TypeScript understand the structure of each list item.
API data can be filtered before it is displayed.
const filteredStudents =
students.filter(student =>
student.name
.toLowerCase()
.includes(search.toLowerCase())
);
<FlatList
data={filteredStudents}
renderItem={renderStudent}
/>
FlatList can request more API data when the user reaches the end of the list.
<FlatList
data={students}
renderItem={renderStudent}
onEndReached={loadMore}
onEndReachedThreshold={0.5}
/>
This is commonly used for infinite scrolling.
onEndReached runs when the list gets close to the end.
async function loadMore() {
if (loadingMore) {
return;
}
await fetchNextPage();
}
The API can then receive the next page number.
GET /api/students.php?page=2
const [loadingMore,
setLoadingMore] =
useState(false);
async function loadMore() {
if (loadingMore) {
return;
}
setLoadingMore(true);
try {
const response =
await fetch(
`${API_URL}?page=${page + 1}`
);
const result =
await response.json();
if (result.success) {
setStudents(
current => [
...current,
...(result.data || [])
]
);
setPage(
current => current + 1
);
}
} finally {
setLoadingMore(false);
}
}
When loading the next page, a footer spinner can be displayed.
ListFooterComponent={
loadingMore ? (
<ActivityIndicator
size="small"
/>
) : null
}
This allows the user to continue seeing the existing records while more data is being loaded.
import React, {
useEffect,
useState
} from "react";
import {
View,
Text,
FlatList,
ActivityIndicator
} from "react-native";
const API_URL =
"https://example.com/api/students.php";
export default function StudentList() {
const [students, setStudents] =
useState([]);
const [loading, setLoading] =
useState(false);
const [error, setError] =
useState("");
async function loadStudents() {
setLoading(true);
setError("");
try {
const response =
await fetch(API_URL);
const result =
await response.json();
if (!response.ok) {
throw new Error(
result.message ||
"Unable to load students"
);
}
if (!result.success) {
throw new Error(
result.message ||
"API request failed"
);
}
setStudents(
result.data || []
);
} catch (error) {
setError(
error.message
);
} finally {
setLoading(false);
}
}
useEffect(() => {
loadStudents();
}, []);
function renderStudent({
item
}) {
return (
<View>
<Text>
Name: {item.name}
</Text>
<Text>
Email: {item.email}
</Text>
<Text>
Course: {item.course}
</Text>
</View>
);
}
if (loading) {
return (
<View>
<ActivityIndicator
size="large"
/>
<Text>
Loading students...
</Text>
</View>
);
}
if (error) {
return (
<View>
<Text>
{error}
</Text>
</View>
);
}
return (
<FlatList
data={students}
keyExtractor={item =>
item.id.toString()
}
renderItem={renderStudent}
ItemSeparatorComponent={() => (
<View
style={{
height: 1
}}
/>
)}
ListHeaderComponent={
<Text>
Student List
</Text>
}
ListEmptyComponent={
<Text>
No students found.
</Text>
}
onRefresh={loadStudents}
refreshing={loading}
/>
);
}
React Native Screen
↓
useEffect()
↓
fetch()
↓
PHP API
↓
MySQL
↓
JSON Data
↓
setStudents()
↓
FlatList
↓
┌──────┴────────┐
↓ ↓
Display Items User Scrolls
↓
onEndReached
↓
Next Page
↓
API Request
↓
Add Items
↓
FlatList
This pattern is the foundation for displaying API-driven lists in React Native applications and can later be extended with search, filtering, sorting, and pagination.
Question: Which FlatList property is used to specify the array of records that should be displayed?