AniUI Academy

ScrollView vs. FlatList

When to reach for ScrollView versus FlatList in React Native — the difference between rendering everything up front and virtualizing a long or unbounded list.

8 min read

Both ScrollView and FlatList let content scroll. The choice between them isn't about which one "scrolls better" — it's about how many items you actually have, and whether that number is known and small, or long-running and effectively unbounded.

ScrollView: renders everything, up front

ScrollView is the simpler of the two conceptually: it takes whatever children you give it and mounts every single one immediately, then lets the whole thing scroll as one container.

import { ScrollView, View, Text, StyleSheet } from "react-native";
 
function SettingsScreen() {
  return (
    <ScrollView contentContainerStyle={styles.content}>
      <View style={styles.row}><Text>Notifications</Text></View>
      <View style={styles.row}><Text>Privacy</Text></View>
      <View style={styles.row}><Text>Appearance</Text></View>
      <View style={styles.row}><Text>Account</Text></View>
      <View style={styles.row}><Text>About</Text></View>
    </ScrollView>
  );
}
 
const styles = StyleSheet.create({
  content: { padding: 16 },
  row: { paddingVertical: 14, borderBottomWidth: 1, borderBottomColor: "#eee" },
});

This is exactly the right tool for a settings screen like this one: the number of rows is small and known ahead of time, so mounting all of them immediately costs nothing meaningful. ScrollView is also the natural choice any time your "list" is really more like a single long page of mixed content — a form, an article, a profile screen with several sections — rather than a repeating collection of same-shaped items.

FlatList: renders only what's near the viewport

FlatList exists for the opposite situation: a list whose length is large, unknown ahead of time, or effectively unbounded — a social feed, a chat history, search results, a product catalog. Mounting every single item immediately, the way ScrollView does, would mean creating potentially thousands of components the user may never scroll to see, which wastes memory and slows everything down.

Instead, FlatList is virtualized: it only actually renders items near the currently visible viewport, mounting new items as the user scrolls toward them and unmounting items that scroll far enough away. From the outside, it looks and scrolls like a normal list — the virtualization is an internal performance detail, not something you manage by hand for basic usage.

import { FlatList, View, Text, StyleSheet } from "react-native";
 
function MessageList({ messages }) {
  return (
    <FlatList
      data={messages}
      keyExtractor={(item) => item.id}
      renderItem={({ item }) => (
        <View style={styles.row}>
          <Text style={styles.author}>{item.author}</Text>
          <Text>{item.text}</Text>
        </View>
      )}
    />
  );
}
 
const styles = StyleSheet.create({
  row: { paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: "#eee" },
  author: { fontWeight: "600" },
});

The three props doing the real work here are the basic FlatList contract:

  • data — the array of items to render. This is your actual list data, exactly as you'd pass an array to .map() in web React.
  • renderItem — a function called for each item, receiving { item } (plus a few other fields you'll rarely need at this level) and returning the JSX for that one row.
  • keyExtractor — a function that returns a stable, unique string key for each item. This is the same concept as the key prop you already know from rendering lists with .map() in web React — FlatList needs it for the same reconciliation reasons a list of JSX elements does.

If your items already have a stable id field, as in the example above, keyExtractor={(item) => item.id} is the standard pattern. Without a stable key, FlatList (like any keyed list) can misbehave across re-renders and scrolling — items appearing to swap content unexpectedly is the classic symptom.

Choosing between them

The decision comes down to one honest question: is the number of items small and effectively fixed, or long/unbounded? A settings screen, a form, a single profile page — ScrollView. A feed, a chat, search results, a product catalog — FlatList. Reaching for ScrollView on a genuinely long list is a common, real performance mistake, because it defeats the entire point of virtualization by forcing everything to mount immediately regardless of what's actually on screen.

This lesson deliberately stays at "which one do I reach for, and what's the basic shape" — deeper virtualization behavior (windowing, batch rendering, performance tuning props) gets its own dedicated lesson later in this track, once you've built more real screens and have a concrete reason to care about that tuning.

What to remember

  • ScrollView mounts every child immediately — fine for a short, known set of items like a settings screen or a form.
  • FlatList is virtualized — it only renders items near the viewport, which is what makes it the right choice for long or unbounded lists.
  • FlatList's basic contract is data (the array), renderItem (turns one item into JSX), and keyExtractor (a stable unique key per item).
  • keyExtractor plays the same role as the key prop on a web React .map() list — reconciliation needs a stable identity per item.
  • Deep virtualization tuning is a separate, later topic — this lesson is about choosing the right component and using its basic API correctly.

Check yourself

3 questions · pass 3/3 to unlock Safe Areas and Responsive Layout

up to 50
  1. 1.What does it mean that ScrollView renders all of its children up front?

  2. 2.What does it mean for FlatList to be 'virtualized'?

  3. 3.In a basic FlatList usage, what are data, renderItem, and keyExtractor each responsible for?

3 left to answer