React Native Performance Grundlagen: Bridge, JSI, neue Architektur
AI generated
</>
{ }
React Native · Performance · JSI · Fabric
React Native Performance Grundlagen
Bridge, JSI und die neue Architektur verstehen

Ruckelnde Listen, verzögerte Touch-Reaktionen und träge Animationen haben in React Native fast immer dieselbe Ursache: zu viel asynchrone Kommunikation zwischen JavaScript und der nativen Seite. Die neue Architektur mit JSI und Fabric hat viele dieser Engpässe strukturell beseitigt, doch Performance bleibt eine Frage der richtigen Techniken im eigenen Code.

20 Min. Lesezeit JSI · Fabric · TurboModules · FlatList React Native 0.76+ · Neue Architektur

1. Warum React Native Performance historisch ein Bridge-Problem war

Wer sich mit React Native Performance beschäftigt, stößt unweigerlich auf den Begriff Bridge, denn die ursprüngliche Architektur von React Native war der wichtigste limitierende Faktor für flüssige Apps. JavaScript lief in einem eigenen Thread, die native Seite mit UI-Rendering in einem anderen, und jede Kommunikation zwischen beiden musste über asynchrone, JSON-serialisierte Nachrichten laufen. Diese Architektur funktionierte gut für gelegentliche Interaktionen, wurde aber zum Engpass, sobald viele Nachrichten pro Sekunde nötig waren, etwa bei Scroll-Events, Gesten oder Animationen.

Das Kernproblem für React Native Performance war die Serialisierung selbst. Jeder Aufruf einer nativen Methode musste in eine JSON-Zeichenkette umgewandelt, über die Bridge geschickt und auf der anderen Seite wieder geparst werden. Bei einfachen, seltenen Aufrufen war dieser Overhead unmerklich. Bei komplexen Animationen mit 60 Bildern pro Sekunde, bei denen jedes Frame mehrere Positions- und Transformationswerte über die Bridge senden musste, summierte sich der Overhead zu spürbarem Jank, also sichtbarem Ruckeln in der Benutzeroberfläche.

Meta hat genau dieses strukturelle Problem mit der sogenannten neuen Architektur angegangen, bestehend aus JSI, Fabric und TurboModules. Diese drei Komponenten ersetzen die Bridge durch direktere Kommunikationswege und bilden seit React Native 0.76 den Standard für neue Projekte. Für React Native Performance bedeutet das: viele frühere Best Practices zur Bridge-Vermeidung sind heute weniger kritisch, während neue Techniken relevant geworden sind.

2. Die alte Bridge-Architektur im Detail

Um React Native Performance-Probleme zu verstehen, hilft ein genauerer Blick auf die klassische Bridge. JavaScript-Code, der beispielsweise StyleSheet.create()-Werte an eine native View übergeben wollte, erzeugte ein Nachrichtenobjekt, das in eine Warteschlange gestellt, batchweise serialisiert und asynchron an die native Seite übermittelt wurde. Diese Asynchronität bedeutete: JavaScript konnte niemals synchron auf einen nativen Wert warten, ohne Callbacks oder Promises zu verwenden, was bestimmte Muster, etwa das synchrone Auslesen einer Layout-Dimension direkt nach dem Rendern, unmöglich oder zumindest fehleranfällig machte.

Der zweite Effekt betraf den Speicherverbrauch: Jede Bridge-Nachricht musste zwischenzeitlich als vollständiges JSON-Objekt im Speicher existieren, selbst für einfache numerische Werte. Bei hoher Nachrichtenfrequenz, etwa während eines schnellen Scroll-Vorgangs mit gleichzeitigen Callback-Aufrufen, konnte die Garbage-Collection-Last spürbar ansteigen und zu zusätzlichen Frame-Drops führen, unabhängig vom eigentlichen JavaScript-Code der Anwendung.

3. JSI: direkter Zugriff statt serialisierter Nachrichten

Das JavaScript Interface, kurz JSI, ist das Fundament der neuen React Native Architektur und der wichtigste Hebel für bessere React Native Performance. Statt Nachrichten zu serialisieren und asynchron zu senden, erlaubt JSI JavaScript, direkte Referenzen auf native C++-Objekte zu halten und deren Methoden synchron aufzurufen, ganz ohne Zwischenschritt über eine Nachrichtenwarteschlange. Diese direkte Bindung eliminiert die Serialisierungskosten für die überwiegende Mehrheit der Interaktionen zwischen JavaScript und nativem Code.

Für Entwickler äußert sich dieser Unterschied vor allem indirekt: Bibliotheken wie Reanimated 3 nutzen JSI, um Animationswerte direkt im UI-Thread zu berechnen, ohne den JavaScript-Thread pro Frame involvieren zu müssen. Das Ergebnis sind Animationen, die auch dann flüssig bei 60 oder 120 Bildern pro Sekunde laufen, wenn der JavaScript-Thread gerade mit anderen Aufgaben, etwa einer Netzwerkantwort-Verarbeitung, beschäftigt ist. Dieser Effekt war unter der alten Bridge-Architektur strukturell unmöglich, weil jede Kommunikation zwangsläufig über den JavaScript-Thread laufen musste.


import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
import { Pressable } from 'react-native';

// Runs entirely on the UI thread via JSI, no bridge round trip per frame
function LikeButton() {
  const scale = useSharedValue(1);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  const handlePress = () => {
    scale.value = withSpring(1.3, {}, () => {
      scale.value = withSpring(1);
    });
  };

  return (
    <Pressable onPress={handlePress}>
      <Animated.View style={animatedStyle}>
        {/* Icon content */}
      </Animated.View>
    </Pressable>
  );
}

4. Fabric: der neue Renderer für synchrone Layouts

Fabric ist der neue Rendering-Layer von React Native, der die Shadow-Tree-Berechnung und die eigentliche native View-Erzeugung enger mit React selbst verzahnt und für erhebliche React Native Performance-Gewinne bei komplexen Layouts sorgt. Anders als der alte Renderer erlaubt Fabric synchrone Layout-Messungen, was bedeutet, dass eine Komponente unmittelbar nach dem Rendern ihre tatsächlichen Dimensionen abfragen kann, ohne auf einen asynchronen Callback über die Bridge warten zu müssen.

Ein weiterer praktischer Vorteil von Fabric betrifft konkurrierende Updates: React 18 und React 19 unterstützen Concurrent Features wie useTransition und Prioritätsstufen für Updates, und Fabric setzt diese Priorisierung tatsächlich bis in den nativen Rendering-Layer durch. Ein hochprioritäres Update, etwa eine Texteingabe, kann dadurch niederpriorisierte Arbeit, etwa das Rendern einer langen Liste im Hintergrund, unterbrechen, was zu spürbar reaktionsschnelleren Eingabefeldern führt, selbst wenn die App gerade viel Rendering-Arbeit im Hintergrund verrichtet.

5. TurboModules: Lazy Loading nativer Module

TurboModules ersetzen das alte Native-Modules-System und verbessern React Native Performance vor allem bei der App-Startzeit. Unter der alten Architektur wurden beim App-Start alle registrierten nativen Module initialisiert, unabhängig davon, ob die App sie tatsächlich in dieser Session verwendete. Bei Apps mit vielen Abhängigkeiten, etwa Zahlungsanbietern, Analytics-SDKs und Kartenbibliotheken, summierte sich diese Initialisierung zu spürbaren Verzögerungen beim Kaltstart.

TurboModules laden native Module hingegen lazy, also erst beim ersten tatsächlichen Zugriff aus JavaScript. Ein Zahlungsmodul, das erst beim Checkout benötigt wird, initialisiert sich dadurch nicht beim App-Start, sondern erst in dem Moment, in dem der Nutzer tatsächlich zur Kasse geht. Bei Apps mit vielen selten genutzten nativen Abhängigkeiten kann dieser Unterschied die wahrgenommene Startzeit um mehrere hundert Millisekunden verkürzen, ein direkter, messbarer Gewinn für React Native Performance ohne jede Änderung am eigenen Anwendungscode.

6. Listen-Performance: FlatList richtig konfigurieren

Lange Listen sind der klassische Härtetest für React Native Performance, und FlatList bietet dafür mehrere Konfigurationsoptionen, die in der Praxis oft ungenutzt bleiben. getItemLayout teilt der Liste die exakten Dimensionen jedes Eintrags im Voraus mit, wodurch FlatList beim Scrollen keine teure Neuberechnung der Layout-Positionen durchführen muss. Diese Optimierung ist besonders wirkungsvoll bei Listen mit einheitlicher Zeilenhöhe, etwa Chat-Nachrichten oder Produktkarten in fester Größe.

windowSize, maxToRenderPerBatch und removeClippedSubviews steuern gemeinsam, wie viele Einträge außerhalb des sichtbaren Bereichs vorgerendert werden, und wie aggressiv nicht sichtbare native Views entfernt werden. Eine zu großzügige Konfiguration dieser Werte verschwendet Speicher und Rechenzeit für Einträge, die der Nutzer nie sieht, während eine zu knappe Konfiguration beim schnellen Scrollen leere weiße Bereiche sichtbar macht, bevor der Inhalt nachgeladen wird. Die richtige Balance hängt stark von der Komplexität der einzelnen Zeilen ab und sollte immer mit dem eigenen Datensatz gemessen werden.


import { FlatList } from 'react-native';

const ITEM_HEIGHT = 72;

// getItemLayout skips expensive layout measurement during scroll
function OrderHistoryList({ orders }) {
  return (
    <FlatList
      data={orders}
      keyExtractor={(item) => item.id}
      renderItem={({ item }) => <OrderRow order={item} />}
      getItemLayout={(_, index) => ({
        length: ITEM_HEIGHT,
        offset: ITEM_HEIGHT * index,
        index,
      })}
      windowSize={5}
      maxToRenderPerBatch={10}
      removeClippedSubviews
      initialNumToRender={8}
    />
  );
}

7. Re-Renders vermeiden: Memoization in der Praxis

Unnötige Re-Renders bleiben auch unter der neuen Architektur eine der häufigsten Ursachen für schlechte React Native Performance, weil jeder Re-Render einer Komponente immer noch JavaScript-Rechenzeit kostet, unabhängig davon, wie effizient die Bridge-Kommunikation inzwischen ist. React.memo für Listen-Elemente verhindert, dass sich eine einzelne Zeile neu rendert, wenn sich nur der übergeordnete Container-State ändert, während die Props der Zeile selbst identisch bleiben.

useCallback für Funktionen, die als Props an memoisierte Kind-Komponenten übergeben werden, verhindert, dass bei jedem Render eine neue Funktionsreferenz erzeugt wird, die React.memo sonst wirkungslos machen würde. Der React Compiler, sofern im Projekt bereits aktiviert, übernimmt einen Großteil dieser manuellen Memoization automatisch, reduziert aber nicht die Notwendigkeit, die Datenstruktur des eigenen State so zu gestalten, dass unnötige Änderungen an der Objektidentität gar nicht erst entstehen.

8. Performance messen statt raten

Jede Optimierung für React Native Performance sollte mit einer Messung beginnen, nicht mit einer Vermutung. Der React DevTools Profiler zeigt, welche Komponenten wie oft und mit welcher Renderdauer neu gerendert werden, und deckt damit häufig überraschende Hotspots auf, etwa eine Kontext-Provider-Komponente, die durch einen häufig wechselnden Wert unnötig viele Kind-Komponenten neu rendern lässt. Zusätzlich liefert der native Flipper- beziehungsweise der neuere React Native DevTools Profiler Einblick in native Rendering-Zeiten, JavaScript-Thread-Auslastung und Speicherverbrauch.

Für Produktions-Apps ist ein kontinuierliches Performance-Monitoring über Dienste wie Sentry oder Firebase Performance sinnvoll, da Entwickler-Geräte oft deutlich leistungsfähiger sind als die tatsächlich im Feld genutzten Geräte der Nutzerbasis. Ein Feature, das auf einem aktuellen Flaggschiff-Smartphone flüssig läuft, kann auf einem drei Jahre alten Mittelklassegerät spürbar ruckeln, ein Unterschied, der ohne Echtnutzer-Metriken oft unentdeckt bleibt.

9. Alte Architektur und neue Architektur im Vergleich

Die folgende Tabelle stellt die zentralen React Native Performance-relevanten Unterschiede zwischen der alten Bridge-Architektur und der neuen Architektur mit JSI, Fabric und TurboModules gegenüber.

Bereich Alte Architektur (Bridge) Neue Architektur Performance-Effekt
JS-Native-Kommunikation Asynchron, JSON-serialisiert Synchron über JSI Kein Serialisierungs-Overhead
Layout-Messung Asynchroner Callback Synchron über Fabric Sofortige Dimensionsabfrage
Module-Initialisierung Alle beim Start Lazy über TurboModules Schnellerer Kaltstart
Animationen im Hintergrund JS-Thread blockiert Animation UI-Thread über JSI/Reanimated Flüssig trotz JS-Last
Update-Priorisierung Nicht unterstützt Concurrent Features via Fabric Reaktionsschnellere Eingaben

Die Tabelle zeigt deutlich: Die neue Architektur löst strukturelle Engpässe, die zuvor unabhängig vom eigenen Anwendungscode existierten. Trotzdem bleiben Listen-Konfiguration, Memoization und Profiling weiterhin Aufgabe des Entwicklungsteams, da keine Architektur schlecht strukturierten Anwendungscode automatisch performant macht.

Mironsoft

React Native Performance-Audits und Optimierung

Ruckelnde Listen und träge Animationen in eurer App?

Wir profilen eure React Native App auf echten Geräten, identifizieren die tatsächlichen Bottlenecks und setzen gezielte Optimierungen um, von Listen-Konfiguration bis zur Migration auf die neue Architektur.

Performance-Audit

Profiling auf echten Geräten mit React Native DevTools und Flipper

Architektur-Migration

Umstieg auf JSI, Fabric und TurboModules ohne Feature-Regression

Listen-Optimierung

FlatList-Konfiguration und Memoization für flüssiges Scrollen

10. Zusammenfassung

Die Grundlagen guter React Native Performance haben sich mit der neuen Architektur strukturell verändert. JSI ersetzt die asynchrone, JSON-serialisierte Bridge durch direkten, synchronen Zugriff auf native Objekte. Fabric bringt synchrone Layout-Messung und echte Update-Priorisierung in den Rendering-Layer. TurboModules laden native Abhängigkeiten lazy und verkürzen dadurch die Kaltstartzeit spürbar. Diese drei Komponenten lösen gemeinsam viele Engpässe, die unter der alten Architektur unabhängig von der Qualität des eigenen Anwendungscodes existierten.

Trotzdem bleibt React Native Performance keine reine Architekturfrage: FlatList-Konfiguration mit getItemLayout, konsequente Memoization mit React.memo und useCallback, sowie kontinuierliches Profiling mit den React DevTools bleiben Pflichtaufgaben für jedes Team. Wer Messung vor Optimierung stellt, vermeidet Zeitverschwendung an Stellen, die für die tatsächliche Nutzererfahrung gar nicht relevant sind.

React Native Performance Grundlagen, das Wichtigste auf einen Blick

JSI

Synchroner, direkter Zugriff auf native Objekte statt serialisierter Bridge-Nachrichten.

Fabric

Neuer Renderer mit synchroner Layout-Messung und echter Update-Priorisierung.

TurboModules

Lazy Loading nativer Module verkürzt die Kaltstartzeit spürbar.

Listen und Memoization

getItemLayout, React.memo und useCallback bleiben unabhängig von der Architektur Pflicht.

11. FAQ: React Native Performance Grundlagen

1Was war das Hauptproblem der alten Bridge?
Asynchrone, JSON-serialisierte Kommunikation zwischen JS und nativer Seite, führte bei hoher Frequenz zu Jank.
2Was ist JSI?
Direkter, synchroner Zugriff auf native C++-Objekte, kein Serialisierungs-Overhead mehr.
3Muss ich migrieren?
Für neue Projekte längst Standard, bestehende Apps profitieren bei Animationen und Startzeit.
4Was macht Fabric anders?
Synchrone Layout-Messung und echte Update-Priorisierung bis in den nativen Renderer.
5Wie helfen TurboModules?
Lazy Loading nativer Module verkürzt die Kaltstartzeit spürbar.
6Warum ruckeln Listen trotz FlatList?
Meist fehlt getItemLayout oder windowSize ist nicht auf die Zeilen-Komplexität abgestimmt.
7Reicht React.memo allein?
Nein, braucht stabile Funktionsprops über useCallback, sonst wirkungslos.
8Warum ist Reanimated flüssiger?
Nutzt JSI für Animationsberechnung im UI-Thread, unabhängig vom JS-Thread.
9Wie profile ich auf echten Geräten?
React DevTools Profiler für Re-Renders, Flipper beziehungsweise React Native DevTools für native Metriken.
10Warum weichen Testgerät und Nutzergeräte ab?
Entwicklergeräte sind meist leistungsfähiger, echtes Nutzer-Monitoring deckt reale Unterschiede auf.