Tab-Navigation-Performance: Lazy Loading von Screens richtig nutzen
AI generated
RN
native
React Native / Navigation
Tab-Navigation-Performance: Lazy Loading von Screens richtig nutzen
Wie verzögertes Rendern inaktiver Tabs die Ladezeit senkt und welcher Trade-off dabei entsteht

Eine Bottom-Tab-Navigation mit fünf oder sechs Tabs rendert ohne bewusste Konfiguration mitunter alle enthaltenen Screens bereits beim allerersten App-Start, obwohl die Nutzerin zunächst nur den ersten Tab überhaupt zu Gesicht bekommt. Lazy Loading verschiebt das Rendern eines Tabs auf den Moment, in dem er tatsächlich zum ersten Mal aktiv wird, und kann die wahrgenommene Startzeit einer Tab-App spürbar verkürzen. Der Effekt hat allerdings einen Preis, wenn ein bereits besuchter Tab beim Wechsel seinen internen Zustand verliert. Dieser Artikel zeigt die praktische Konfiguration, den konkreten Trade-off und wie sich der Effekt tatsächlich messen lässt.

11 Min. Lesezeit Tab-Navigation Lazy Loading Performance

1. Was Lazy Loading bei Tab-Navigatoren konkret bedeutet

Ohne Lazy Loading initialisiert ein Bottom-Tab-Navigator standardmäßig alle konfigurierten Tab-Screens gleichzeitig beim ersten Rendern des Navigators, selbst wenn nur ein einziger Tab tatsächlich sichtbar ist. Jeder dieser Screens durchläuft dabei den vollständigen React-Lifecycle inklusive aller enthaltenen useEffect-Hooks, wodurch beispielsweise ein Datenladevorgang im vierten Tab bereits ausgeführt wird, obwohl die Nutzerin diesen Tab möglicherweise nie öffnet.

Mit aktiviertem Lazy Loading wird ein Tab-Screen erst dann tatsächlich gemountet, wenn er zum ersten Mal aktiv ausgewählt wird, wodurch sich die Menge an Arbeit beim initialen App-Start auf genau den Tab reduziert, der tatsächlich sichtbar ist. Bereits besuchte Tabs bleiben nach dem ersten Mounten grundsätzlich im Speicher erhalten, sodass ein erneuter Wechsel zu einem zuvor bereits geöffneten Tab kein erneutes Mounten auslöst, sondern lediglich die Sichtbarkeit ändert.

2. Standardverhalten versus explizite Konfiguration

In neueren Versionen von @react-navigation/bottom-tabs ist Lazy Loading bereits standardmäßig aktiviert, was in vielen bestehenden Projekten dazu führt, dass Entwicklerinnen und Entwickler den Effekt gar nicht bewusst konfiguriert haben, sondern ihn implizit bereits nutzen. Dennoch lohnt sich eine explizite Konfiguration über die lazy-Option am jeweiligen Tab.Navigator, weil dadurch im Code sichtbar dokumentiert wird, dass dieses Verhalten bewusst gewählt und nicht zufällig vorhanden ist.

Für einzelne Tabs, bei denen ein sofortiges Vorladen tatsächlich gewünscht ist, etwa ein Home-Tab mit Daten, die ohnehin unmittelbar nach dem Öffnen der App benötigt werden, lässt sich Lazy Loading gezielt über die lazy-Option auf Ebene des einzelnen Screens deaktivieren, während es für alle übrigen Tabs aktiv bleibt. Diese gemischte Konfiguration ist in der Praxis häufig sinnvoller als eine pauschale Ein- oder Ausschaltung für den gesamten Navigator.


import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

const Tab = createBottomTabNavigator();

export function MainTabs() {
  return (
    <Tab.Navigator screenOptions={{ lazy: true }}>
      <Tab.Screen
        name="Home"
        component={HomeScreen}
        options={{ lazy: false }} // bewusst sofort geladen
      />
      <Tab.Screen name="Search" component={SearchScreen} />
      <Tab.Screen name="Orders" component={OrdersScreen} />
      <Tab.Screen name="Account" component={AccountScreen} />
    </Tab.Navigator>
  );
}

3. Der Trade-off: Zustandsverlust beim Wechsel zwischen Tabs

Lazy Loading betrifft ausschließlich den Zeitpunkt des ersten Mountens und nicht das Verhalten bei nachfolgenden Tab-Wechseln, was häufig zu einem Missverständnis führt: Standardmäßig bleibt ein einmal gemounteter Tab-Screen dauerhaft im Speicher, auch wenn er nicht sichtbar ist, wodurch sein interner Zustand wie eine Formulareingabe oder eine Scroll-Position beim Zurückwechseln erhalten bleibt. Wird zusätzlich detachInactiveScreens oder eine ähnliche Optimierung über react-native-screens aktiviert, werden inaktive Screens dagegen tatsächlich aus dem nativen View-Baum entfernt, was Speicher spart, aber gleichzeitig lokalen Komponentenzustand zerstören kann.

Dieser Unterschied ist entscheidend für die Entscheidung, welche Optimierung für welchen Tab sinnvoll ist: Ein Tab mit einer langen, unveränderlichen Liste profitiert von einer aggressiven Speicherbereinigung, weil ein Neuaufbau kaum spürbar ist, während ein Tab mit einem teilweise ausgefüllten Formular oder einer aufwendig gescrollten Position durch dieselbe Optimierung eine spürbar schlechtere Nutzererfahrung erzeugen kann, wenn die Eingaben beim erneuten Öffnen plötzlich verschwunden sind.

4. freezeOnBlur und detachInactiveScreens im Detail

react-native-screens stellt mit freezeOnBlur eine feingranularere Alternative zum vollständigen Entfernen eines Screens bereit: Statt den Screen komplett zu unmounten, werden lediglich alle Re-Renders und Effekte pausiert, während der native View weiterhin im Speicher und im View-Baum vorhanden bleibt. Dadurch bleibt der komplette React-Zustand, einschließlich Formulareingaben und Scroll-Position, vollständig erhalten, während gleichzeitig unnötige Hintergrund-Renderarbeit vermieden wird, was in den meisten Fällen den besten Kompromiss zwischen Speicherverbrauch und Zustandserhalt darstellt.

Die aggressivere Option detachInactiveScreens entfernt inaktive Screens dagegen vollständig aus dem nativen View-Baum, was den Speicherverbrauch bei Apps mit sehr vielen Tabs oder sehr speicherintensiven Screens spürbar senkt, aber wie beschrieben jeglichen lokalen Zustand verwirft. Ein sinnvoller Mittelweg für die meisten Produktions-Apps besteht darin, freezeOnBlur global zu aktivieren und detachInactiveScreens nur gezielt für einzelne, bekanntermaßen speicherhungrige Tabs einzuschalten, statt es pauschal für die gesamte Navigation zu setzen.

5. Praktische Konfiguration im Code

Die Konfiguration von freezeOnBlur erfolgt über die screenOptions des Tab-Navigators und wirkt für alle enthaltenen Screens einheitlich, sofern kein einzelner Screen dies explizit überschreibt. Für gezieltes detachInactiveScreens auf Ebene eines einzelnen Stack-Navigators innerhalb eines Tabs lässt sich die Option direkt an diesem verschachtelten Navigator setzen, sodass nur der jeweilige Bereich betroffen ist und der Rest der App unverändert bleibt.

In der Praxis empfiehlt es sich, diese Konfiguration an einer zentralen Stelle im Navigations-Setup zu dokumentieren, etwa mit einem Kommentar, der begründet, warum ein bestimmter Tab von der Standardkonfiguration abweicht, damit spätere Änderungen am Navigationsbaum nicht versehentlich eine bewusst getroffene Performance-Entscheidung rückgängig machen.


import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { enableFreeze, enableScreens } from 'react-native-screens';

enableScreens();
enableFreeze(true); // globale Voreinstellung für alle Screens

const Tab = createBottomTabNavigator();

export function MainTabs() {
  return (
    <Tab.Navigator screenOptions={{ lazy: true, freezeOnBlur: true }}>
      <Tab.Screen name="Home" component={HomeScreen} options={{ lazy: false }} />
      <Tab.Screen name="Feed" component={FeedScreen} />
      {/* Feed enthaelt viele Bilder, hier lohnt sich gezieltes Detach */}
      <Tab.Screen
        name="Media"
        component={MediaStack}
        options={{ freezeOnBlur: false, detachInactiveScreens: true }}
      />
    </Tab.Navigator>
  );
}

6. Lazy-Verhalten der Tabs-Komponente in Expo Router

Die Tabs-Komponente von Expo Router baut intern auf demselben Bottom-Tab-Navigator von React Navigation auf und übernimmt dessen Lazy-Loading-Verhalten größtenteils unverändert, wodurch sich dieselben Optionen wie lazy und freezeOnBlur auch dort über screenOptions setzen lassen. Der Unterschied liegt hauptsächlich darin, dass Expo Router die Tab-Screens automatisch aus der Ordnerstruktur im app/(tabs)-Verzeichnis ableitet, statt sie manuell als Tab.Screen-Elemente aufzulisten, was die Konfiguration syntaktisch etwas anders aussehen lässt, ohne das zugrunde liegende Verhalten zu verändern.

Weil Expo Router zusätzlich URL-basiertes Routing mit der Tab-Navigation kombiniert, ist besonders relevant, dass ein direkter Deep Link zu einem tief liegenden Tab-Screen dessen Lazy-Loading-Verhalten umgeht, weil der Ziel-Tab in diesem Fall sofort gemountet werden muss, um den Link korrekt darzustellen. Dieses Verhalten ist beabsichtigt, sollte bei der Performance-Planung aber berücksichtigt werden, weil ein per Deep Link geöffneter Tab nicht von der verzögerten Initialisierung profitiert.

7. Speicherverbrauch gegen Ladezeit abwägen

Bei einer App mit nur drei oder vier eher einfachen Tabs bringt eine aggressive Speicheroptimierung durch detachInactiveScreens häufig kaum spürbaren Nutzen, weil der zusätzliche Speicherverbrauch aller gemounteten Tabs ohnehin gering bleibt, während der Trade-off in Form von verlorenem Zustand bei jedem Tab-Wechsel spürbar negativ auffällt. Bei einer App mit acht oder mehr Tabs, von denen einige aufwendige Listen, Karten oder Medien-Grids enthalten, kehrt sich diese Abwägung häufig um, weil der Speicherverbrauch aller gleichzeitig aktiven Screens tatsächlich zu spürbaren Rucklern oder sogar App-Abstürzen auf älteren Geräten führen kann.

Als praktische Faustregel eignet sich eine Kategorisierung der eigenen Tabs in drei Gruppen: Tabs mit unveränderlichem, leicht neu ladbarem Inhalt profitieren von aggressivem Detachment, Tabs mit wichtigem, schwer wiederherstellbarem lokalem Zustand sollten freezeOnBlur statt Detachment nutzen, und ein einzelner, besonders häufig genutzter Startbildschirm-Tab sollte in der Regel ganz von Lazy Loading ausgenommen werden.

8. Den Effekt tatsächlich messen statt nur zu vermuten

Um den Effekt von Lazy Loading objektiv zu belegen, eignet sich ein einfacher Vergleich der Time-to-Interactive-Metrik über das React Native Performance Monitor Overlay oder ein dediziertes Tool wie react-native-performance, gemessen jeweils einmal mit vollständig deaktiviertem Lazy Loading und einmal mit der Standardkonfiguration. Besonders aussagekräftig ist eine Messung auf einem tatsächlich älteren Mittelklasse-Android-Gerät, weil der Unterschied auf einem aktuellen High-End-Gerät durch dessen hohe Rohleistung häufig kaum wahrnehmbar ist, während er auf schwächerer Hardware deutlich sichtbar wird.

Zusätzlich zur reinen Ladezeit lohnt sich eine Beobachtung der Speicherbelegung über die nativen Profiling-Werkzeuge von Android Studio beziehungsweise Xcode Instruments, um zu prüfen, ob eine gewählte Kombination aus lazy, freezeOnBlur und detachInactiveScreens tatsächlich den erwarteten Effekt erzielt, statt sich ausschließlich auf die Dokumentation der jeweiligen Bibliothek zu verlassen, deren Verhalten sich zwischen Versionen durchaus ändern kann.

9. Kombination mit Suspense und Datenladen pro Tab

Lazy Loading auf Navigator-Ebene und Datenladen innerhalb eines Screens lassen sich sinnvoll kombinieren, indem ein Tab-Screen erst beim eigenen Mounten, ausgelöst durch das erstmalige Aktivieren des Tabs, seinen zugehörigen Datenabruf startet, statt Daten für alle Tabs bereits beim App-Start vorzuladen. In Kombination mit React Suspense und einer entsprechenden Datenbibliothek lässt sich dieser Ladevorgang zusätzlich mit einem Skeleton-Screen visuell abfedern, sodass der ohnehin unvermeidbare erste Ladevorgang eines Tabs nicht als spürbare Verzögerung wahrgenommen wird.

Wichtig ist dabei, den Datenabruf nicht bei jedem erneuten Sichtbarwerden eines bereits gemounteten Tabs zu wiederholen, sondern ihn über einen geeigneten Cache-Mechanismus, etwa React Query oder SWR, an das erste Mounten zu binden und bei nachfolgenden Tab-Wechseln lediglich die zwischengespeicherten Daten erneut anzuzeigen, solange sie nicht als veraltet markiert wurden.

Option Effekt Zustand nach Tab-Wechsel Empfohlen für
lazy: true Screen mountet erst bei erster Aktivierung Bleibt nach erstem Mounten erhalten Alle Tabs außer dem Startbildschirm
lazy: false Screen mountet sofort beim App-Start Immer erhalten Häufig genutzter Home-Tab
freezeOnBlur: true Re-Renders und Effekte pausiert, View bleibt bestehen Vollständig erhalten Tabs mit Formularen oder wichtiger Scroll-Position
detachInactiveScreens: true Screen komplett aus dem View-Baum entfernt Wird verworfen Speicherintensive Tabs mit unveränderlichem Inhalt
Suspense + Cache pro Tab Datenabruf an erstes Mounten gebunden Zwischengespeicherte Daten bleiben sichtbar Tabs mit eigenem Datenladevorgang

Mironsoft

React-Native-App-Entwicklung und Magento-Anbindung

Eine mobile App zum Magento-Shop, die wirklich rund läuft?

Wir entwickeln React-Native-Apps, die sauber an die Magento REST- oder GraphQL-API angebunden sind, von der ersten Codezeile bis zur Veröffentlichung im App Store und bei Google Play.

App-Konzeption

Architektur und Feature-Umfang einer Magento-angebundenen App gemeinsam planen.

Magento-API-Integration

Produktkatalog, Warenkorb und Checkout sauber an die Shop-API anbinden.

Store-Veröffentlichung

App Store- und Google-Play-Freigabeprozess ohne Stolperfallen begleiten.

10. Zusammenfassung

Tab-Navigation-Performance: Das Wichtigste auf einen Blick

Effekt

Lazy Loading verschiebt das Mounten eines Tabs auf den Moment seiner ersten Aktivierung und senkt so die initiale Ladezeit.

Trade-off

Aggressive Speicheroptimierung wie detachInactiveScreens verwirft lokalen Zustand beim Tab-Wechsel.

Mittelweg

freezeOnBlur pausiert Re-Renders ohne den Zustand zu verlieren und ist meist der beste Kompromiss.

Messung

Der Effekt zeigt sich vor allem auf älteren Mittelklasse-Geräten, weniger auf aktuellen High-End-Geräten.

11. FAQ: Tab-Navigation-Performance: Das Wichtigste auf einen Blick

1Was passiert ohne Lazy Loading in einem Tab-Navigator?
Ohne Lazy Loading werden standardmäßig alle konfigurierten Tab-Screens gleichzeitig beim ersten Rendern des Navigators gemountet, selbst wenn nur ein einziger Tab tatsächlich sichtbar ist. Jeder dieser Screens durchläuft dabei den vollständigen React-Lifecycle inklusive aller enthaltenen Effekte.
2Ist Lazy Loading in aktuellen React-Navigation-Versionen standardmäßig aktiv?
In neueren Versionen von @react-navigation/bottom-tabs ist Lazy Loading bereits standardmäßig aktiviert. Eine explizite Konfiguration über die lazy-Option lohnt sich trotzdem, um das Verhalten im Code sichtbar zu dokumentieren.
3Was ist der Unterschied zwischen freezeOnBlur und detachInactiveScreens?
freezeOnBlur pausiert lediglich Re-Renders und Effekte eines inaktiven Screens, während der native View und der komplette React-Zustand erhalten bleiben. detachInactiveScreens entfernt den Screen dagegen vollständig aus dem nativen View-Baum und verwirft dabei jeglichen lokalen Zustand.
4Welche Tabs eignen sich für aggressives Detachment?
Tabs mit unveränderlichem, leicht neu ladbarem Inhalt wie einer langen, statischen Liste profitieren von detachInactiveScreens, weil ein Neuaufbau beim erneuten Öffnen kaum spürbar ist und der gesparte Speicher den Verlust an Zustand rechtfertigt.
5Warum sollte ein Home-Tab häufig von Lazy Loading ausgenommen werden?
Ein Home-Tab wird meist unmittelbar nach dem App-Start benötigt und von der Nutzerin sofort betrachtet, sodass eine verzögerte Initialisierung keinen Vorteil bringt, aber ein kurzes Aufblitzen eines Ladezustands verursachen kann. Deshalb lohnt sich hier häufig lazy: false.
6Wie verhält sich Lazy Loading bei der Tabs-Komponente von Expo Router?
Die Tabs-Komponente baut intern auf demselben Bottom-Tab-Navigator auf und übernimmt dessen Lazy-Loading-Verhalten größtenteils unverändert, mit denselben Optionen über screenOptions. Ein Deep Link zu einem tief liegenden Tab-Screen umgeht das Lazy Loading, weil der Ziel-Tab sofort gemountet werden muss.
7Wie lässt sich der Effekt von Lazy Loading objektiv messen?
Über einen Vergleich der Time-to-Interactive-Metrik mit dem React Native Performance Monitor oder react-native-performance, einmal mit deaktiviertem und einmal mit aktiviertem Lazy Loading. Besonders aussagekräftig ist die Messung auf einem älteren Mittelklasse-Android-Gerät statt auf aktueller High-End-Hardware.
8Wann lohnt sich Lazy Loading kaum?
Bei Apps mit nur drei oder vier eher einfachen Tabs bringt aggressive Speicheroptimierung häufig kaum spürbaren Nutzen, weil der zusätzliche Speicherverbrauch aller gemounteten Tabs ohnehin gering bleibt, während der Verlust an Zustand bei jedem Tab-Wechsel spürbar negativ auffällt.
9Wie lässt sich Lazy Loading mit eigenem Datenladen pro Tab kombinieren?
Ein Tab-Screen startet seinen Datenabruf erst beim eigenen ersten Mounten statt beim App-Start, idealerweise mit React Suspense und einem Skeleton-Screen zur visuellen Abfederung. Ein Cache-Mechanismus wie React Query verhindert, dass die Daten bei jedem erneuten Tab-Wechsel neu geladen werden.
10Kann Lazy Loading für einzelne Tabs unterschiedlich konfiguriert werden?
Ja, die lazy-Option lässt sich sowohl global über screenOptions am Navigator als auch gezielt an jedem einzelnen Tab.Screen setzen, wobei die Einstellung am einzelnen Screen die globale Vorgabe überschreibt. Dieselbe Möglichkeit besteht auch für freezeOnBlur und detachInactiveScreens.