React Native

React Native Navigation State Lost on App Reload

Losing navigation state on reload is expected default behavior in React Navigation — it doesn't persist state automatically, so if you need the user's position preserved across reloads (development refreshes or actual app restarts), it has to be configured explicitly.

The Problem

Every time the app reloads — whether from a development hot reload, a full app restart, or after being backgrounded and resumed on some devices — the navigation stack resets to the initial screen, losing whatever screen the user was actually on.

Why It Happens

React Navigation keeps its state entirely in memory by default, with no built-in persistence unless you explicitly configure it. This is by design, since not every app wants navigation state persisted (a login flow, for example, usually shouldn't restore mid-flow after a restart). Common reasons this becomes a problem:

  • No persistence configuration exists at all, so state is simply lost by design on every reload, which is expected but often not what the developer actually wants during active development
  • Persistence is configured, but the storage mechanism used to save state isn't actually completing the write before the app closes or reloads, especially with slower or improperly awaited storage calls
  • The navigation container is being recreated with a fresh key or a completely new instance on each reload, bypassing whatever state restoration logic exists
  • State was saved successfully, but the restoration logic has a bug preventing it from actually being read back and applied when the app starts again

The Fix

To persist and restore navigation state properly, use React Navigation's built-in support for this via the NavigationContainer's initialState and onStateChange props, combined with AsyncStorage:

import AsyncStorage from '@react-native-async-storage/async-storage';
import { NavigationContainer } from '@react-navigation/native';

const PERSISTENCE_KEY = 'NAVIGATION_STATE';

function App() {
  const [isReady, setIsReady] = useState(false);
  const [initialState, setInitialState] = useState();

  useEffect(() => {
    const restoreState = async () => {
      const savedStateString = await AsyncStorage.getItem(PERSISTENCE_KEY);
      const state = savedStateString ? JSON.parse(savedStateString) : undefined;
      setInitialState(state);
      setIsReady(true);
    };
    restoreState();
  }, []);

  if (!isReady) return null;

  return (
    <NavigationContainer
      initialState={initialState}
      onStateChange={(state) =>
        AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state))
      }
    >
      {/* your navigators */}
    </NavigationContainer>
  );
}

This pattern saves the navigation state to AsyncStorage every time it changes, and restores it before the app renders any screens, so a reload picks up exactly where the user left off.

Be deliberate about when you actually want this behavior. Restoring navigation state unconditionally can create confusing situations — for example, restoring a user into the middle of a checkout flow or a multi-step form after an unrelated app crash. Many apps intentionally disable persistence in production while keeping it enabled only during development, where quickly returning to the screen you were testing is a genuine convenience:

const initialState = __DEV__ ? savedState : undefined;

If you're only trying to solve this for local development convenience (not real users), this dev-only conditional is often the simplest and safest approach, since it avoids introducing persistence complexity into your production navigation logic at all.

Still Not Working?

If you've implemented persistence correctly but state still isn't restoring, add logging directly around both the save and restore calls to confirm each is actually executing and producing the expected data, rather than assuming based on the code alone:

onStateChange={(state) => {
  console.log('Saving state:', state);
  AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state));
}}

If the save log never appears, the issue is in triggering the save (check that onStateChange is actually wired to the right container). If the save appears but restoration doesn't produce the expected screen, check for a mismatch between the saved state's screen/route names and your current navigator configuration — renaming a screen after state was previously saved can result in a saved state that references routes that no longer exist, silently failing to restore correctly.