Mobile OSes aggressively kill backgrounded apps to reclaim memory. Without state persistence, a user who navigated three screens deep, switched to Maps for directions, and came back lands on a cold splash screen with their place lost — breaking the session and dumping any in-progress form input. This is especially punishing on iOS where low-memory terminations are silent, and it corrupts any analytics funnel that assumes continuous sessions.
Medium because state loss is only visible after background kills but severely degrades the session-continuity experience.
Persist navigation state to AsyncStorage on change and restore it on mount before rendering NavigationContainer. Put this in src/navigation/RootNavigator.tsx:
import AsyncStorage from '@react-native-async-storage/async-storage'
const [initialState, setInitialState] = useState()
const [isReady, setIsReady] = useState(false)
useEffect(() => {
AsyncStorage.getItem('NAV_STATE').then((s) => {
if (s) setInitialState(JSON.parse(s))
setIsReady(true)
})
}, [])
if (!isReady) return null
return (
<NavigationContainer
initialState={initialState}
onStateChange={(s) => AsyncStorage.setItem('NAV_STATE', JSON.stringify(s))}
>
<RootNavigator />
</NavigationContainer>
)
ID: mobile-navigation-linking.state-management.persist-on-background
Severity: medium
What to look for: Test the app by navigating to a nested screen, backgrounding the app (pressing home), and returning. Check whether the app returns to the same screen. Look for NavigationContainer.onStateChange persisting state to storage and restoration logic in the app's initialization.
Pass criteria: Count all navigation state persistence mechanisms (onStateChange + AsyncStorage, or equivalent). When the app is backgrounded and reopened, the user is returned to the same screen they were on, with the same navigation stack preserved. At least 1 persistence mechanism must exist using onStateChange callback with a storage write.
Fail criteria: Backgrounding the app resets navigation to the home screen or splash screen. Users lose their place in the app. No more than 0 backgrounding scenarios should result in navigation state loss.
Skip (N/A) when: The app does not require navigation state persistence (fewer than 3 screens).
Detail on fail: "App resets to home screen when backgrounded and reopened" or "Navigation state is not persisted between sessions"
Remediation: Persist and restore navigation state across app backgrounding:
import AsyncStorage from '@react-native-async-storage/async-storage'
const RootNavigator = () => {
const [isReady, setIsReady] = useState(false)
const [initialState, setInitialState] = useState()
useEffect(() => {
const restoreState = async () => {
try {
const savedState = await AsyncStorage.getItem('navigationState')
if (savedState) {
setInitialState(JSON.parse(savedState))
}
} finally {
setIsReady(true)
}
}
restoreState()
}, [])
if (!isReady) return null
return (
<NavigationContainer
initialState={initialState}
onStateChange={state => {
AsyncStorage.setItem('navigationState', JSON.stringify(state))
}}
>
{/* navigators */}
</NavigationContainer>
)
}