Hussain Arif Hussain is a CS student in Pakistan whose biggest interest is learning and teaching programming to make the world a better place.

A deep dive into React Native FlatList

7 min read 2184

A Deep Dive into React Native FlatList

Consider a situation where you want to display a list of items from an API. For example, the Coffee API’s response looks like this:

Coffee API List Response

One possible way to display this to the client is to use the map method on this array like so:

const data = getDataFromAPI(); 
return (
  <View style={styles.container}>
    {data && (
      <View>
        <Text> Your data has loaded! Here is the result:</Text>
        {data.map((item) => (
          <Text key={item.id}> {item.name}</Text>
        ))}
      </View>
    )}
  </View>
);

Here, we are using conditional rendering to check if the data has loaded. If true, we will notify the user and display the list on the screen.
This will be the output:

Coffee API Data Loaded but List is Not Visible

The code should work. So why isn’t our list visible?

This is where FlatList comes in to mitigate this problem. It is a React Native component that allows you to render lists with zero hassle and minimal code.

FlatList syntax

FlatList uses the following syntax:

import { FlatList } from "react-native";
<FlatList
  data={
    //the array to render
  }
  keyExtractor={
    // Extract keys for each item in the array
  }
  renderItem={
    //each item from the array will be rendered here
  }
/>;
  • In the data prop, you will enter the array that you want to display. This can be JSON data from an API
  • The keyExtractor prop will retrieve a unique key for each item in the array
    N.B., if your array contains a key or id field, you don’t need to include this prop. By default, FlatList will look for the key or id property.
  • renderItem will tell React Native how to render the items from the list

Sample usage

Now that we’ve covered the syntax, let’s use it to render our list:

//the data array contains our array of items. 
const data = [{id:1, title:"Black", description:""},... ]; 
const Item = ({ title, description }) => (
  <View>
    <Text style={styles.title}>{title} </Text>
    <Text>{description} </Text>
  </View>
);

const renderItem = ({ item }) => (
  <Item title={item.title} description={item.description} />
);
return (
  <View style={styles.container}>
    {data && (
      <FlatList
        data={data}
        renderItem={renderItem}
        keyExtractor={(item) => item.id}
      />
    )}
  </View>
);

Let’s deconstruct this code piece by piece:

We made a custom demo for .
No really. Click here to check it out.

  • The Item component will accept two props: title and description. This component will display them to the UI
  • The renderItem function will render the Item component for every item in the array. As a result, this will display the title and description fields from each object
  • Moreover, the keyExtractor prop tells React Native that it should use the id field as a key
  • In the end, we used conditional rendering to render the data with the FlatList React Native element

Sample FlatList with Coffee API

Displaying data from an API

In the real world, React Native developers might have to deal with rendering API data into lists.

In the code below, we are fetching data from the Coffee API and plugging it into our FlatList component:

export default function App() {
  const [data, setData] = useState(null);

  const getData = async () => {
    const resp = await fetch("https://api.sampleapis.com/coffee/hot");
    const data = await resp.json();
    setData(data);
  };
  //on first mount, fetch data.
  useEffect(() => {
    getData();
  }, []);

  const Item = ({ title, description, index }) => (
    <View>
      <Text style={styles.title}>
        {index}. {title}
      </Text>
      <Text> {description} </Text>
    </View>
  );

  const renderItem = ({ item, index }) => (
    <Item description={item.description} title={item.title} index={index} />
  );

  return (
    <View style={styles.container}>
      {data && (
        <FlatList
          data={data}
          renderItem={renderItem}
        />
      )}
    </View>
  );
}

A few inferences from this code:

  • When the App component is first rendered, React invokes the getData method. This will make a GET request to the API
  • In the renderItem function, we are also using the index argument. This is the index corresponding to the current item in the array
  • Notice that we aren’t using the keyExtractor prop. This is because an id field is already present, so FlatList will use this as the key

RN FlatList Displaying Data from an API

FlatList customization

Header component

FlatList also has support for header components. This can be handy in cases where you want to display a search bar on the top of an inventory of contacts.
The ListHeaderComponent prop can help you do that:

import { Divider } from "react-native-elements";

const header = () => {
  return (
    <View>
      <Text style={styles.title}> Coffee list</Text>
      <Divider orientation="vertical" />
    </View>
  );
};

return (
  <View style={styles.container}>
    {data && (
      <FlatList
        ListHeaderComponent={header}
        data={data}
        renderItem={renderItem}
      />
    )}
  </View>
);

In the above block of code, we first created a header function and then passed it into our ListHeaderComponent prop. This will output a simple Text and Divider component at the top of the FlatList component.

FlatList Header Component

Footer component

Consider a situation where the user has scrolled all the way down and wants to go back up. Here, it would be sensible to show a button that would take the client back up automatically. In this case, you could append a footer component for your list. This will indicate that the list has concluded.

You can do it via the ListFooterComponent prop like so:

//This will be our footer component
const endComponent = () => {
  return (
    <View>
      <Divider orientation="vertical" />
      <Text style={styles.text}> List ended</Text>
    </View>
  );
};

return (
  <View style={styles.container}>
    {data && (
      <FlatList
        ListFooterComponent={endComponent}
        data={data}
        renderItem={renderItem}
      />
    )}
  </View>
);

FlatList Footer Component

Separators

Separator components help the user distinguish each element in the list. This contributes to a better user experience and interface.

To achieve this, use the ItemSeparatorComponent prop like so:

const separator = () => {
  return <Divider orientation="vertical" />;
};

return (
  <View style={styles.container}>
    {data && (
      <FlatList
        ItemSeparatorComponent={separator}
        data={data}
        renderItem={renderItem}
      />
    )}
  </View>
);

RN FlatList Separators

Handling empty lists

Let’s say that you have built a note-taking app in React Native. If the user has no records, it would be suitable to show a “No notes found” message.

For this, the ListEmptyComponent prop is useful:

const data = []; //empty array
const handleEmpty = () => {
  return <Text style={styles.title}> No data present!</Text>;
};
return (
  <View style={styles.container}>
    {!data && <Text> Loading</Text>}
    {data && (
      <FlatList
        ListEmptyComponent={handleEmpty}
        data={data}
        renderItem={renderItem}
      />
    )}
  </View>
);

RN FlatList Empty List

FlatList navigation methods

FlatList also includes a few utility functions for scroll-related operations.

Scroll to end

Assume that you have a list containing hundreds of items. Here, it would be good to display a button that lets the user go all the way down. This will result in a better user experience since the user doesn’t have to manually scroll to the bottom, thus saving time.

To make this possible, you can use the scrollToEnd method like so:

const list = useRef(null);

const press = () => {
  list.current.scrollToEnd({ animated: true });
};
const header = () => {
  return <Button onPress={() => press()} title="Go to end" />;
};

return (
  <View style={styles.container}>
    {!data && <Text> Loading</Text>}
    {data && (
      <FlatList
        ref={list}
        ListHeaderComponent={header}
        data={data}
        renderItem={renderItem}
      />
    )}
  </View>
);

In this piece of code, we created a useRef Hook which will grant us access to FlatList’s utility functions. When the user clicks on the Button element, the app will execute the scrollToEnd method.

FlatList Scroll to End Button

Navigating to a specific index item

We can even scroll to a specific item by calling the scrollToIndex method like so:

const list = useRef(null);

const press = () => {
  //scroll to the 12th item in the index.
  list.current.scrollToIndex({ animated: true, index: 12 });
};

const header = () => {
  return <Button onPress={() => press()} title="Go to 12th index" />;
};

return (
  <View style={styles.container}>
    {!data && <Text> Loading</Text>}
    {data && (
      <FlatList
        ref={list}
        ListHeaderComponent={header}
        data={data}
        renderItem={renderItem}
      />
    )}
  </View>
);

In this code snippet, we told React that if the user clicks on the button, then it should redirect the user to the twelfth item in the array.

Navigating to a Specific Index

Best practices

Avoid anonymous functions

Try to avoid using anonymous functions in your FlatList. The React Native team suggests this so that the function won’t recreate itself every time your list is displayed. This saves memory and CPU resources:

//correct way: no anonymous functions
const renderItem = ({ item, index }) => (
  <Text>
    {index}. {item.title}
  </Text>
);
{
  data && (
    <FlatList
      data={data}
      keyExtractor={(item) => item.id}
      renderItem={renderItem}
    />
  );
}
//the wrong way. Steer clear from this:
{
  data && (
    <FlatList
      data={data}
      keyExtractor={(item) => item.id}
      renderItem={({ item, index }) => (
        <Text>
          {index}.{item.title}
        </Text>
      )}
    />
  );
}

Lighter components

The heavier your components are, the more memory they take. Display as little information as you can on your list item. As a bonus, this makes your UI look cleaner.

Even if you do have items that need to display a lot of text, you can use React Navigation. If the user taps on any item, React will direct them to a separate page that will show the remaining details. Here is an example:

RN FlatList with Too Much TextRN FlatList with Read More

Common problems and questions

Images do not render

To solve this problem, check if you are loading images from the local storage or from the network (an API). React Native’s Image module has a slightly different syntax for each use case.

The code sample below renders images from the file system:

const apiData = [
  {
    id: 1,
    title: "The Simpsons",
    year: 1989,
    image: require("./simpson.jpg"), 
  },
  {
    id: 2,
    title: "SpongeBob SquarePants ",
    year: 1999,
    image: require("./spongebob.jpg"),
  },
];

const renderItem = ({ item, index }) => (
  <View>
    <Text style={styles.title}>{item.title} </Text>
    <Text> {item.year}</Text>
    <Image
      style={{ height: 300, width: 300}}
      source={item.image}
      resizeMode="contain"
    />
  </View>
);
return (
  <View style={styles.container}>
    <FlatList
      data={apiData}
      keyExtractor={(item) => item.id.toString()}
      renderItem={renderItem}
    />
  </View>
);

Notice that we are using the require method. This tells React Native to fetch images from the local file system.

Render Images with RN FlatList

To display images from the network, use the uri property for your prop like so:

const apiData = [
  {
    id: 1,
    title: "The Simpsons",
    year: 1989,
    //url for image
    image:
      "https://m.media-amazon.com/images/M/MV5BYjFkMTlkYWU[email protected]@._V1_SY1000_CR0,0,666,1000_AL_.jpg",
  },
  {
    id: 2,
    title: "SpongeBob SquarePants ",
    year: 1999,
    image:
      "https://nick.mtvnimages.com/uri/mgid:arc:content:nick.com:9cd2df6e-63c7-43da-8bde-8d77af9169c7?quality=0.7",
  },
];
const renderItem = ({ item, index }) => (
  <View>
    <Text style={styles.title}>{item.title} </Text>
    <Text> {item.year}</Text>
    <Image
      style={{ height: 300, width: 300 }}
      source={{ uri: item.image }} //using uri property
      resizeMode="contain"
    />
  </View>
);
return (
  <View style={styles.container}>
      <FlatList
        data={apiData}
        keyExtractor={(item) => item.id.toString()}
        renderItem={renderItem}
      />
  </View>
);

Inverting the list

To reverse the item order, simply use the inverted prop:

return (
  <View style={styles.container}>
    <FlatList
      data={apiData}
      keyExtractor={(item) => item.id}
      renderItem={renderItem}
      inverted //reverses the list
    />
  </View>
);

The list should re-render when a variable changes

To solve this issue, use the extraData prop. This will tell FlatList to render whenever the chosen variable updates:

var changedData = 0;
//other logic-related code..
 return (
    <View style={styles.container}>
        <FlatList
          data={apiData}
          extraData={changedData}
          keyExtractor={(item) => item.id}
          renderItem={renderItem}
        />
    </View>
  );

This will now tell React Native to listen for changes in the changedData variable. When a change is detected, React will re-render the list.

Performance issues when rendering a large number of items

You can use the maxToRenderPerBatch prop. This specifies the number of items rendered per batch, which is the next group of items displayed on every scroll:

return (
  <FlatList 
    data={data}
    extraData={changedData}
    keyExtractor={(item) => item.id}
    renderItem={renderItem}
    maxToRenderPerBatch={5} //render only 5 items per scroll.
)

Why not use a ScrollView instead?

The ScrollView component renders all the items in one go. This might be fine for small lists, but not for lists with hundreds of items.

For example, take a note-taking app. If your app renders the components and views them all at once, this will result in increased memory usage.

The FlatList module handles things differently. It will only render items when they are about to appear and deletes them when they are off view. This results in lower memory usage.

In short, FlatList uses lazy loading, which is better for optimization purposes.

Conclusion

In this article, we covered React Native’s FlatList usage, its useful functions and customization options. It is a crucial component if you want to render lists in your React Native app. It is an absolute breeze to use and is rock solid.

Thank you for reading! Happy coding.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Hussain Arif Hussain is a CS student in Pakistan whose biggest interest is learning and teaching programming to make the world a better place.

3 Replies to “A deep dive into React Native FlatList”

  1. Certainly everything but a deep dive.. you just showed an example of usage. As for FlatList, the most buggy component in all React Native, I highly recommend you not to talk about FlatList as a “rock solid” component. I was excepting a deep dive into the component itself and how to avoid at least the rendering issues, the images not loading within the FlatList items etc.. just a very beginner’s guide, which is great for beginners, but change the title, it’s totally misleading.

    1. Hi Vahradrim, thank you for taking the time to read my post! I didn’t research properly and that’s my fault.
      I’ve now updated my article and would like to know your thoughts.

Leave a Reply