FlashList v2 in React Native: Performante Listen für die New Architecture

FlashList v2 ist der schnellste Weg, lange Listen in React Native darzustellen. Alles zur Installation, FlatList-Migration, Masonry-Layouts und den neuen Hooks – mit praktischen Codebeispielen für die New Architecture.

Warum FlashList v2 ein Gamechanger für React-Native-Listen ist

Hand aufs Herz: Wer in React Native mit langen Listen arbeitet – sei es ein Social-Media-Feed, ein Produktkatalog oder ein Chat-Verlauf –, der kennt die Schmerzen von FlatList. Ruckelndes Scrollen auf Android, leere Bereiche beim schnellen Wischen und ein JS-Thread, der einfach nicht zur Ruhe kommt. Kommt dir bekannt vor?

Shopifys FlashList hat sich mit über 2 Millionen monatlichen Downloads als performante Alternative etabliert. Und jetzt kommt der nächste große Schritt.

Mit FlashList v2 hat Shopify einen kompletten Neuschrieb veröffentlicht, der speziell für die New Architecture (Fabric) von React Native optimiert ist. Die Ergebnisse sind ehrlich gesagt beeindruckend: Bis zu 50 % weniger leere Bereiche beim Scrollen, keine nativen Abhängigkeiten mehr, automatische Größenermittlung ohne Schätzwerte und neue Features wie Masonry-Layouts und pixelgenaues Scrollen – alles als reine JavaScript-Lösung.

In diesem Guide zeig ich dir Schritt für Schritt, wie du FlashList v2 in deinem React-Native-Projekt einsetzt. Von der Installation über die Migration von FlatList bis hin zu Masonry-Layouts und den neuen Hooks – alles was du brauchst, um richtig performante Listen zu bauen.

Voraussetzungen und Installation

Bevor wir loslegen, ein wichtiger Punkt: FlashList v2 wurde ausschließlich für die New Architecture von React Native entwickelt. Die Legacy-Architektur wird nicht unterstützt. Falls dein Projekt noch die alte Bridge-basierte Architektur nutzt, musst du erstmal bei FlashList v1 bleiben (oder du nutzt die Gelegenheit und migrierst gleich).

Systemanforderungen

  • React Native 0.76+ mit aktivierter New Architecture (Fabric)
  • Expo SDK 55+ (bei Expo-Projekten – New Architecture ist dort mittlerweile Standard)
  • React 18.2+ oder React 19.x
  • iOS 15.1+ und Android 7+

Installation

Für Expo-Projekte:

npx expo install @shopify/flash-list

Für React Native CLI-Projekte:

npm install @shopify/flash-list@^2.0.0
# oder
yarn add @shopify/flash-list@^2.0.0

Und das Beste daran: Da FlashList v2 eine reine JS-Lösung ohne native Abhängigkeiten ist, entfällt das nervige pod install auf iOS. Einfach installieren und loslegen – kein natives Setup nötig.

FlatList vs. FlashList: Warum der Wechsel lohnt

Um zu verstehen, warum FlashList so viel schneller ist, lohnt sich ein Blick auf die grundlegend unterschiedlichen Architekturen.

Virtualisierung vs. Cell Recycling

FlatList nutzt Virtualisierung: Elemente außerhalb des sichtbaren Bereichs werden zerstört und beim Scrollen komplett neu erstellt. Bei kleinen Listen geht das noch, aber bei großen Datenmengen entsteht ein enormer Aufwand an View-Erzeugung und Garbage Collection – und das merkt man als Frame-Drops.

FlashList macht es schlauer mit Cell Recycling: Statt Views zu zerstören, werden sie einfach wiederverwendet. Scrollt ein Listenelement aus dem sichtbaren Bereich, wird dieselbe View-Instanz mit neuen Daten befüllt und woanders wieder eingesetzt. Weniger Speicherverbrauch, weniger CPU-Last, keine leeren Bereiche.

Gemessene Verbesserungen

Die Unterschiede sind nicht nur Theorie – sie sind messbar (und ziemlich drastisch):

  • FPS beim Scrollen: Von durchschnittlich 36,9 FPS (FlatList) auf 56,9 FPS (FlashList) – das sind 54 % mehr
  • JS-Thread-Auslastung: Von über 90 % auf unter 10 % – eine Reduktion um satte 82 %
  • CPU-Gesamtauslastung: Von 198,9 % auf 36,5 %
  • Out-of-Memory-Abstürze: Komplett eliminiert

Gerade auf Low-End-Android-Geräten ist der Unterschied krass. Wo FlatList mit einstelligen FPS-Werten kämpft, liefert FlashList flüssige 60 FPS. Ich hab das in einem Kundenprojekt selbst erlebt – der Unterschied war sofort spürbar.

Schnelleinstieg: Deine erste FlashList

FlashList ist ein Drop-in-Replacement für FlatList. Die API ist praktisch identisch, sodass die Migration meistens nur ein paar Sekunden dauert.

Einfaches Beispiel

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

interface Produkt {
  id: string;
  name: string;
  preis: number;
}

const PRODUKTE: Produkt[] = [
  { id: '1', name: 'React Native Kurs', preis: 49.99 },
  { id: '2', name: 'TypeScript Masterclass', preis: 39.99 },
  { id: '3', name: 'Expo Bootcamp', preis: 59.99 },
  // ... hunderte weitere Einträge
];

const ProduktItem = ({ item }: { item: Produkt }) => (
  
    {item.name}
    {item.preis.toFixed(2)} €
  
);

export default function ProduktListe() {
  return (
     }
      keyExtractor={(item) => item.id}
    />
  );
}

const styles = StyleSheet.create({
  item: {
    padding: 16,
    borderBottomWidth: 1,
    borderBottomColor: '#e0e0e0',
  },
  name: {
    fontSize: 16,
    fontWeight: '600',
  },
  preis: {
    fontSize: 14,
    color: '#666',
    marginTop: 4,
  },
});

Das war's. Keine estimatedItemSize-Prop mehr (die in v1 noch Pflicht war), kein natives Setup – FlashList v2 ermittelt die Größen einfach automatisch.

Migration von FlatList

Die Migration ist wirklich simpel – in den allermeisten Fällen reicht es, den Import und den Komponentennamen zu tauschen:

// Vorher
import { FlatList } from 'react-native';

 }
  keyExtractor={(item) => item.id}
/>

// Nachher
import { FlashList } from '@shopify/flash-list';

 }
  keyExtractor={(item) => item.id}
/>

Wichtig: Entferne explizite key-Props aus der renderItem-Hierarchie. FlashList verwaltet Keys intern über den keyExtractor. Doppelte Keys können zu ziemlich unerwartetem Verhalten beim Recycling führen – das ist eine der häufigsten Stolperfallen.

Was ist neu in FlashList v2?

FlashList v2 ist kein einfaches Update – es ist ein kompletter Neuschrieb. Also schauen wir uns die wichtigsten Neuerungen mal im Detail an.

Keine Größenschätzungen mehr

In v1 musste man estimatedItemSize angeben, damit die Komponente die Scrollbar und den Render-Bereich berechnen konnte. In v2 ist das Geschichte. FlashList misst die Elemente jetzt automatisch beim ersten Rendern und cached die Ergebnisse. Weniger API-Oberfläche, weniger Fehlerquellen – win-win.

Reine JavaScript-Lösung

Das gesamte Recycling-System ist jetzt in JavaScript implementiert, ohne native Abhängigkeiten. Möglich wurde das durch die Verbesserungen im Layout-System der New Architecture. Was das in der Praxis bedeutet: einfachere Installation, weniger Build-Probleme und konsistentes Verhalten auf iOS, Android und Web.

Adaptives Render-Fenster

Statt eines festen Render-Fensters wie in v1 kommt jetzt ein adaptiver Algorithmus zum Einsatz. Der berücksichtigt Scroll-Geschwindigkeit und -Richtung und passt den Draw-Buffer dynamisch an. Scrollst du schnell nach unten, wird mehr Inhalt unterhalb des sichtbaren Bereichs vorgerendert. Auf langsameren Geräten passt sich der Buffer an die tatsächlichen Renderzeiten an. Ziemlich clever.

Pixelgenaues Scrollen

In v1 war das Scrollen zu einem bestimmten Element oft ungenau (was je nach Anwendungsfall richtig nervig sein konnte). Das neue System in v2 nutzt progressive Verfeinerung: Beim Annähern an das Ziel wird die Position mehrfach gemessen und korrigiert, bis sie pixelgenau stimmt.

Verbesserte horizontale Listen

Horizontale Listen wurden grundlegend überarbeitet. Elemente können jetzt beliebige Größen haben und die Listenhöhe passt sich automatisch an. Besonders praktisch: Wenn horizontale FlashLists in einer vertikalen FlashList verschachtelt sind, koordinieren sie sich automatisch für optimale Lade- und Renderzeiten.

Fortgeschritten: Masonry-Layouts erstellen

So, jetzt wird's richtig spannend. Eines der coolsten Features in FlashList v2 ist die native Unterstützung für Masonry-Layouts – also Pinterest-ähnliche Raster, bei denen Elemente unterschiedliche Höhen haben dürfen.

Einfaches Masonry-Layout

In v2 aktivierst du das Masonry-Layout einfach über eine Prop:

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

interface BildItem {
  id: string;
  bildUrl: string;
  titel: string;
  hoehe: number;
}

const BILDER: BildItem[] = [
  { id: '1', bildUrl: 'https://picsum.photos/200/300', titel: 'Sonnenuntergang', hoehe: 200 },
  { id: '2', bildUrl: 'https://picsum.photos/200/150', titel: 'Berge', hoehe: 150 },
  { id: '3', bildUrl: 'https://picsum.photos/200/250', titel: 'Meer', hoehe: 250 },
  { id: '4', bildUrl: 'https://picsum.photos/200/180', titel: 'Wald', hoehe: 180 },
  // ... weitere Einträge
];

const BildKarte = ({ item }: { item: BildItem }) => (
  
    
    {item.titel}
  
);

export default function BilderGalerie() {
  return (
     }
      keyExtractor={(item) => item.id}
      optimizeItemArrangement
    />
  );
}

const styles = StyleSheet.create({
  karte: {
    margin: 4,
    borderRadius: 12,
    overflow: 'hidden',
    backgroundColor: '#f5f5f5',
  },
  bild: {
    flex: 1,
    width: '100%',
  },
  bildTitel: {
    padding: 8,
    fontSize: 14,
    fontWeight: '500',
  },
});

Die Prop optimizeItemArrangement (standardmäßig aktiviert) sorgt dafür, dass Elemente so verteilt werden, dass Höhenunterschiede zwischen den Spalten minimiert werden. Das sieht einfach besser aus.

Masonry mit variablen Spaltenbreiten

Für noch komplexere Layouts gibt's overrideItemLayout, um einzelnen Elementen unterschiedliche Column-Spans zu geben:

 {
    // Hervorgehobene Elemente über 2 Spalten
    layout.span = item.hervorgehoben ? 2 : 1;
  }}
  renderItem={({ item }) => }
  keyExtractor={(item) => item.id}
/>

Kurzer Hinweis: Im Gegensatz zu v1 wird overrideItemLayout in v2 nur noch für layout.span verwendet. Größenschätzungen über layout.size sind nicht mehr nötig.

Neue Hooks: useLayoutState und useRecyclingState

FlashList v2 bringt zwei neue Hooks mit, die das Arbeiten mit dynamischen Listeneinträgen deutlich einfacher machen. Schauen wir sie uns an.

useLayoutState – Layout-bewusster State

useLayoutState funktioniert im Grunde wie useState, kommuniziert Änderungen aber zusätzlich an FlashList. Dadurch werden Layout-Neuberechnungen ausgelöst, wenn sich die Größe eines Elements ändert – zum Beispiel bei aufklappbaren Accordions oder "Mehr anzeigen"-Buttons.

import { useLayoutState } from '@shopify/flash-list';

const AufklappbarerEintrag = ({ item }) => {
  const [aufgeklappt, setAufgeklappt] = useLayoutState(false);

  return (
     setAufgeklappt(!aufgeklappt)}>
      
        {item.titel}
        {aufgeklappt && (
          
            {item.details}
          
        )}
      
    
  );
};

Ohne useLayoutState würde FlashList die Größenänderung erst über den onLayout-Callback mitbekommen – was zu sichtbaren Glitches führen kann. Mit dem Hook wird die Layout-Änderung synchron kommuniziert. Ein kleiner Unterschied mit großer Wirkung.

useRecyclingState – Automatischer State-Reset beim Recycling

useRecyclingState kombiniert useLayoutState mit einem automatischen State-Reset, wenn sich die Abhängigkeiten ändern. Das brauchst du für item-spezifischen State, der beim Recycling zurückgesetzt werden muss.

import { useRecyclingState } from '@shopify/flash-list';

const NachrichtenItem = ({ item }) => {
  const [erweitert, setErweitert] = useRecyclingState(
    false,
    [item.id],  // State wird zurückgesetzt, wenn sich item.id ändert
    () => {
      // Optionaler Callback nach dem Reset
      // Nützlich z.B. zum Zurücksetzen von Scroll-Positionen
    }
  );

  return (
     setErweitert(!erweitert)}>
      
        {item.absender}
        
          {item.nachricht}
        
      
    
  );
};

Das Problem dahinter: Wenn FlashList eine View recycelt und einem neuen Datensatz zuweist, bleibt lokaler State normalerweise erhalten. Ohne useRecyclingState könnte ein aufgeklapptes Element aufgeklappt bleiben, obwohl es jetzt völlig andere Daten anzeigt. Der Hook setzt den State automatisch zurück, sobald sich die Abhängigkeiten (hier item.id) ändern – und das ohne zusätzlichen Render-Zyklus. Ziemlich elegant gelöst, finde ich.

Praxisbeispiel: Chat-Liste mit Infinite Scrolling

Jetzt wird's konkret. Lass uns alles kombinieren, was wir bisher gelernt haben: Eine Chat-Nachrichtenliste mit Infinite Scrolling nach oben (ältere Nachrichten laden) und automatischer Scroll-Position-Erhaltung.

import React, { useState, useCallback, useRef } from 'react';
import { View, Text, ActivityIndicator, StyleSheet } from 'react-native';
import { FlashList, FlashListRef } from '@shopify/flash-list';

interface Nachricht {
  id: string;
  text: string;
  absender: string;
  zeitstempel: Date;
  istEigen: boolean;
}

export default function ChatListe() {
  const [nachrichten, setNachrichten] = useState(INITIAL_NACHRICHTEN);
  const [laedt, setLaedt] = useState(false);
  const listeRef = useRef>(null);

  const aeltereNachrichtenLaden = useCallback(async () => {
    if (laedt) return;
    setLaedt(true);

    const neueDaten = await fetchAeltereNachrichten(nachrichten[0]?.id);
    setNachrichten((prev) => [...neueDaten, ...prev]);
    setLaedt(false);
  }, [laedt, nachrichten]);

  const renderNachricht = useCallback(({ item }: { item: Nachricht }) => (
    
      {!item.istEigen && (
        {item.absender}
      )}
      {item.text}
      
        {item.zeitstempel.toLocaleTimeString('de-DE', {
          hour: '2-digit',
          minute: '2-digit',
        })}
      
    
  ), []);

  return (
    
      {laedt && }
       item.id}
        inverted={false}
        onStartReached={aeltereNachrichtenLaden}
        onStartReachedThreshold={0.5}
        getItemType={(item) => (item.istEigen ? 'eigen' : 'fremd')}
      />
    
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#f0f0f0' },
  ladeAnzeige: { padding: 10 },
  nachricht: {
    maxWidth: '75%',
    padding: 12,
    marginVertical: 4,
    marginHorizontal: 12,
    borderRadius: 16,
  },
  eigenNachricht: {
    alignSelf: 'flex-end',
    backgroundColor: '#007AFF',
  },
  fremdNachricht: {
    alignSelf: 'flex-start',
    backgroundColor: '#FFFFFF',
  },
  absender: { fontSize: 12, fontWeight: '600', marginBottom: 4 },
  nachrichtenText: { fontSize: 15 },
  zeit: { fontSize: 11, color: '#999', marginTop: 4, textAlign: 'right' },
});

Ein paar v2-Features fallen hier besonders auf:

  • onStartReached – Das ist neu in v2. Wird aufgerufen, wenn der Nutzer zum Anfang der Liste scrollt. Perfekt für Chat-Apps, die ältere Nachrichten nachladen müssen.
  • maintainVisibleContentPosition – In v2 standardmäßig aktiviert. Hält die Scroll-Position stabil, wenn am Anfang neue Elemente eingefügt werden. Ohne das würde die Liste bei jedem Nachladen wild springen.
  • getItemType – Trennt die Recycling-Pools für verschiedene Element-Typen. Eigene und fremde Nachrichten werden getrennt recycelt, was Layout-Shifts verhindert.
  • FlashListRef – Der neue Ref-Typ in v2, der den direkten FlashList-Typ aus v1 ersetzt.

Migration von FlashList v1 zu v2

Du nutzt bereits FlashList v1? Dann ist die Migration zu v2 überschaubar – aber es gibt ein paar Dinge, die du anpassen musst.

Entfernte Props

Diese Props aus v1 müssen raus, weil sie in v2 schlicht nicht mehr existieren:

  • estimatedItemSize – Nicht mehr nötig, FlashList misst jetzt automatisch
  • estimatedListSize – Entfernt
  • estimatedFirstItemOffset – Entfernt
  • onBlankArea – Entfernt (leere Bereiche treten in v2 kaum noch auf, daher überflüssig)
  • disableHorizontalListHeightMeasurement – Entfernt
  • disableAutoLayout – Entfernt

Masonry-Migration

Der MasonryFlashList-Komponent aus v1 ist veraltet. Nimm stattdessen die masonry-Prop auf der regulären FlashList:

// v1 – Veraltet
import { MasonryFlashList } from '@shopify/flash-list';


// v2 – Empfohlen
import { FlashList } from '@shopify/flash-list';

Ref-Typ aktualisieren

// v1
import { FlashList } from '@shopify/flash-list';
const ref = useRef>(null);

// v2
import { FlashList, FlashListRef } from '@shopify/flash-list';
const ref = useRef>(null);

CellContainer ersetzen

Der CellContainer-Export wurde in v2 entfernt. Verwende einfach die reguläre View-Komponente von React Native – fertig.

Performance-Tipps für FlashList v2

Auch wenn FlashList v2 out-of-the-box deutlich schneller ist als FlatList, gibt's ein paar Best Practices, die du kennen solltest.

1. Props memoisieren

In v2 ist das Memoisieren von Props sogar noch wichtiger als in v1. Verwende useCallback für renderItem und keyExtractor:

const renderItem = useCallback(({ item }) => (
  
), []);

const keyExtractor = useCallback((item) => item.id, []);

2. keyExtractor immer verwenden

Ein valider keyExtractor ist in v2 dringend empfohlen. Ohne ihn kann es zu visuellen Glitches kommen, besonders beim Scrollen nach oben oder bei Layout-Änderungen. Das ist schnell vergessen, aber macht einen großen Unterschied.

3. getItemType für heterogene Listen

Wenn deine Liste verschiedene Element-Typen enthält (z. B. Bilder, Texte, Werbebanner), verwende getItemType für separate Recycling-Pools. So verhinderst du teure Layout-Shifts, wenn ein Bildelement als Textelement recycelt wird:

 item.typ} // 'bild', 'text', 'werbung'
  keyExtractor={(item) => item.id}
/>

4. maxItemsInRecyclePool kontrollieren

Bei Listen mit vielen verschiedenen Element-Typen kann der Recycling-Pool ziemlich groß werden. Mit maxItemsInRecyclePool begrenzt du die Anzahl vorgehaltener Views pro Typ:

 item.typ}
  maxItemsInRecyclePool={5}
/>

5. React.memo für renderItem-Komponenten

Wickle deine Listenelemente in React.memo ein, damit sie nur bei tatsächlichen Datenänderungen neu gerendert werden:

const MeinItem = React.memo(({ item }: { item: MeinTyp }) => (
  
    {item.titel}
  
));

MeinItem.displayName = 'MeinItem';

Häufig gestellte Fragen

Funktioniert FlashList v2 mit der alten React-Native-Architektur?

Nein. FlashList v2 ist ausschließlich für die New Architecture (Fabric) gebaut. Für die Legacy-Architektur bleibst du bei FlashList v1.x. Mit Expo SDK 55 ist die New Architecture Standard, sodass neue Expo-Projekte FlashList v2 direkt nutzen können.

Kann ich FlashList v2 mit Expo verwenden?

Ja, absolut. FlashList v2 funktioniert problemlos mit Expo SDK 55 und höher. Installieren mit npx expo install @shopify/flash-list und loslegen – kein zusätzliches Setup nötig, da FlashList v2 keine nativen Dependencies hat.

Was genau ist der Unterschied zwischen FlashList und FlatList?

Der Kern-Unterschied liegt in der Rendering-Strategie: FlatList zerstört und erstellt Views neu (Virtualisierung), FlashList verwendet sie wieder (Cell Recycling). Das Ergebnis sind bis zu 5-10x bessere Performance-Werte, vor allem auf Android. FlashList v2 bietet darüber hinaus automatische Größenermittlung, Masonry-Layouts und einen adaptiven Render-Algorithmus.

Muss ich estimatedItemSize in v2 noch angeben?

Nein, die Prop gibt's in v2 gar nicht mehr. FlashList v2 misst alle Elemente automatisch beim ersten Rendern und cached die Ergebnisse. Ehrlich gesagt war estimatedItemSize in v1 eine der häufigsten Fehlerquellen – gut, dass das jetzt weg ist.

Wie gehe ich mit verschiedenen Element-Typen in einer Liste um?

Verwende die getItemType-Prop für separate Recycling-Pools. FlashList recycelt dann nur innerhalb desselben Typs, was Layout-Shifts und unnötige Re-Renders verhindert. Bei vielen verschiedenen Typen kombinierst du das am besten mit maxItemsInRecyclePool, um den Speicherverbrauch im Griff zu behalten.

Über den Autor Editorial Team

Our team of expert writers and editors.