skip to content

In React Navigation 7, why can a Help screen rendered for both signed-in and signed-out users stay open after sign-out, and how does navigationKey fix it?

level: seniorimportance: should knowfreq 27%

answer

  1. still-defined routes survive the flip
  2. Help focused, SignIn missing
  3. key change removes or resets
  4. Group navigationKey tied to auth
  5. non-empty string or undefined

basics

~20 s

The stack keeps every route whose screen is still defined, so a shared Help route survives sign-out with no SignIn beneath it. A navigationKey that changes with auth state makes React Navigation remove or reset those routes.

solid answer

~40 s

When the auth state flips, the stack router removes only routes whose screen names are no longer rendered. A `Help` screen declared outside the auth conditional is rendered in both states, so if it was focused at sign-out its route survives: the stack holds just `Help`, possibly with the signed-in patient's context in its params or component state, and `SignIn` is nowhere in history. Giving the screen, or a `Stack.Group` of shared screens, `navigationKey={isSignedIn ? 'user' : 'guest'}` fixes it: when the key changes, React Navigation treats those routes as stale — the stack router removes them, a tab router recreates them fresh. With nothing valid left, the stack starts on `SignIn`. The key must be a non-empty string or `undefined`; anything else throws.

code

tsx · 17 lines
tsx
<Stack.Navigator>
  {isSignedIn ? (
    <Stack.Group>
      <Stack.Screen name="Home" component={HomeScreen} />
      <Stack.Screen name="VisitRoom" component={VisitRoomScreen} />
    </Stack.Group>
  ) : (
    <Stack.Group>
      <Stack.Screen name="SignIn" component={SignInScreen} />
      <Stack.Screen name="SignUp" component={SignUpScreen} />
    </Stack.Group>
  )}
  <Stack.Group navigationKey={isSignedIn ? 'user' : 'guest'}>
    <Stack.Screen name="Help" component={HelpScreen} />
    <Stack.Screen name="PrivacyPolicy" component={PrivacyPolicyScreen} />
  </Stack.Group>
</Stack.Navigator>

go deeper

for a junior

Know that screens declared outside the auth condition exist in both states, and that React Navigation has a navigationKey prop for resetting them when auth changes.

for a middle

Trace the stack router: surviving routes are those whose screens are still rendered, and only an empty stack falls back to the initial screen.

for a senior

Name the privacy consequence of a surviving shared screen, key shared groups on auth state or user id, and explain why a manual reset in one handler is fragile.

for a principal

Treat screen identity across sessions as a policy: decide which surfaces may carry state across accounts and encode it declaratively rather than in scattered handlers.

## The failure A telehealth app often has screens that make sense whether or not the patient is signed in: `Help`, `PrivacyPolicy`, `ContactSupport`. The natural code declares them once, **outside** the auth conditional, so both branches can reach them. Now trace a sign-out in **React Navigation 7**. The patient is signed in with the stack `Home` → `Help` and taps "Sign out" from a menu while `Help` is focused. The auth state flips and the navigator re-renders with the new route names: `SignIn`, `SignUp`, `Help`, `PrivacyPolicy`. The stack router then: 1. Keeps the routes whose screens are still defined — `Help` is, `Home` is not. 2. Finds one valid route left, so it does **not** fall back to the initial screen. 3. Clamps the focused index to that single route. The patient is now signed out but looking at `Help`, with `SignIn` nowhere in the history. There is nothing to go back to inside this stack. If the help screen showed anything tied to the session — the patient's name, an open support case, a draft message kept in component state — it is still on screen for whoever picks the phone up next. In a health app that is a privacy defect, not just an odd navigation state. ## What navigationKey does Every `Screen` and every `Group` accepts an optional **`navigationKey`**. React Navigation's own description is that if the key changes, existing screens with that name are removed or reset. The mechanics: - For each screen name, the navigator records the list of keys on the screen and its enclosing groups. - On re-render, any screen whose key list differs from last time is reported to the router as a **key change**. - The **stack router** filters those routes out, exactly as if the screen had disappeared. If no route is left, it starts on `initialRouteName` (when rendered) or the first screen. - The **tab router** (bottom tabs) replaces a changed tab's route with a fresh one, so its screen state resets while the tab itself remains. Tie the key to the auth state and a sign-out now clears `Help` too: ```tsx <Stack.Group navigationKey={isSignedIn ? 'user' : 'guest'}> <Stack.Screen name="Help" component={HelpScreen} /> <Stack.Screen name="PrivacyPolicy" component={PrivacyPolicyScreen} /> </Stack.Group> ``` Traced again: `Home` is gone because it is undefined, `Help` is gone because its key changed from `'user'` to `'guest'`, no route is left, and the stack starts on `SignIn`. ## Rules and edges | Aspect | Behaviour | |---|---| | Accepted values | A non-empty string, or `undefined`; any other value throws an "invalid 'navigationKey' prop" error | | Uniqueness | Not required — many screens and groups can share one key | | Where to put it | On a single `Screen`, or on a `Group` to cover all its shared screens | | Nested groups | A screen's identity combines the keys of all enclosing groups and its own | | Static configuration | Each entry under `groups` is rendered with its object key as the group's `navigationKey` | Because only strings are accepted, `navigationKey={isSignedIn}` with a boolean throws; convert it, for example `String(isSignedIn)` as React Navigation's own auth example does, or use explicit names such as `'user'` and `'guest'`. ## Choosing the key - If the shared screen should never carry session state across sign-out, key it on the auth state as above. - If different patients can sign in on one device, keying on a **user id** also resets shared screens when one account replaces another without passing through the signed-out branch. - If a shared screen is genuinely stateless (a static policy page), leaving it unkeyed is harmless, but it still blocks the fall-back to `SignIn` when it is focused, so keying it is usually simpler than reasoning about it. ## Why interviewers ask it The first-order auth pattern — render one branch or the other — is well known. This is the second-order bug that appears only when a real app adds shared screens, and it tests whether the candidate knows the router's rule precisely: routes survive when their screen is still defined, not when the user "should" still see them. A strong answer traces the stack before and after, names the privacy consequence, and reaches for `navigationKey` rather than a manual `reset` in the sign-out handler.

  • Why not just call navigation.reset to SignIn in the sign-out handler?
    It fixes one entry point. Sign-out can also come from a token-expiry handler, a server 401 or another device, and each would need the same reset. It also fights the conditional pattern: the router already rebuilds history when the screen list changes. `navigationKey` states the rule once, next to the screens it protects.
  • How does navigationKey behave on a bottom tab navigator?
    The tab router keeps a tab for every rendered screen, so a key change does not remove the tab; it replaces that tab's route with a fresh one, resetting its screen state and nested history. In a stack, the same key change removes the route.

Like re-coding a hotel's shared lounge door at checkout: the lounge still exists for the next guest, but the old guest's keycard — and whatever they left inside — no longer carries over.

saying these in an interview costs you the question

  • A screen rendered in both branches is removed on sign-out like every other screen.
  • navigationKey must be unique per screen, like a React list key.
  • Passing the isSignedIn boolean directly as navigationKey works fine.
  • Changing navigationKey only re-renders the screen in place and keeps its route.
  • The fix is to reset navigation to SignIn inside the sign-out button handler.