Skip to main content

SectionList

React Native has a convenience component on top of FlatList, called SectionList. This component has some additional props:

FlashList offers none of these props but all of them are replaceable with existing props.

The difficulty of migrating from SectionList to FlashList will depend on the data you have at hand - the data may be more suitable for SectionList, requiring you to massage the data, but the opposite can be true as well. In that case, using FlashList instead of SectionList might even result in less code.

Let's go through how to migrate from SectionList to FlashList in the following example - a contacts list.

This is how we could write such a list with SectionList:

import React from "react";
import { SectionList, StyleSheet, Text } from "react-native";

interface Contact {
firstName: string;
lastName: string;
}

interface Section {
title: string;
data: Contact[];
}

const contacts: Section[] = [
{ title: "A", data: [{ firstName: "John", lastName: "Aaron" }] },
{
title: "D",
data: [
{ firstName: "John", lastName: "Doe" },
{ firstName: "Mary", lastName: "Dianne" },
],
},
];

const ContactsSectionList = () => {
return (
<SectionList
sections={contacts}
renderItem={({ item }) => {
return <Text>{item.firstName}</Text>;
}}
renderSectionHeader={({ section: { title } }) => (
<Text style={styles.header}>{title}</Text>
)}
/>
);
};

const styles = StyleSheet.create({
header: {
fontSize: 32,
backgroundColor: "#fff",
},
});

To migrate to FlashList, we'd need to first convert the contacts variable to the following:

const contacts: (string | Contact)[] = [
"A",
{ firstName: "John", lastName: "Aaron" },
"D",
{ firstName: "John", lastName: "Doe" },
{ firstName: "Mary", lastName: "Dianne" },
];

As you can see, you can add the section item right along with the data. Then in the renderItem, you can distinguish what to render based on the type of the item:

const ContactsFlashList = () => {
return (
<FlashList
data={contacts}
renderItem={({ item }) => {
if (typeof item === "string") {
// Rendering header
return <Text style={styles.header}>{item}</Text>;
} else {
// Render item
return <Text>{item.firstName}</Text>;
}
}}
getItemType={(item) => {
// To achieve better performance, specify the type based on the item
return typeof item === "string" ? "sectionHeader" : "row";
}}
estimatedItemSize={100}
/>
);
};

You can follow a similar pattern as for renderItem for the rest of the SectionList's props.

If you want your section headers to be sticky, you will also need to compute the array for stickyHeaderIndices:

const stickyHeaderIndices = contacts
.map((item, index) => {
if (typeof item === "string") {
return index;
} else {
return null;
}
})
.filter((item) => item !== null) as number[];

And that's it! Below you can find the whole example for FlashList:

import React from "react";
import { StyleSheet, Text } from "react-native";
import { FlashList } from "@shopify/flash-list";

interface Contact {
firstName: string;
lastName: string;
}

const contacts: (string | Contact)[] = [
"A",
{ firstName: "John", lastName: "Aaron" },
"D",
{ firstName: "John", lastName: "Doe" },
{ firstName: "Mary", lastName: "Dianne" },
];

const stickyHeaderIndices = contacts
.map((item, index) => {
if (typeof item === "string") {
return index;
} else {
return null;
}
})
.filter((item) => item !== null) as number[];

const ContactsFlashList = () => {
return (
<FlashList
data={contacts}
renderItem={({ item }) => {
if (typeof item === "string") {
// Rendering header
return <Text style={styles.header}>{item}</Text>;
} else {
// Render item
return <Text>{item.firstName}</Text>;
}
}}
stickyHeaderIndices={stickyHeaderIndices}
getItemType={(item) => {
// To achieve better performance, specify the type based on the item
return typeof item === "string" ? "sectionHeader" : "row";
}}
estimatedItemSize={100}
/>
);
};

const styles = StyleSheet.create({
header: {
fontSize: 32,
backgroundColor: "#fff",
},
});