skip to content

Conditional Auth Flows

React Navigation swaps signed-in and signed-out screens by rendering them conditionally, not by navigating after login. Interviewers probe the restore-token splash and what history is left.

part ofReact Nativeoverview, primer and where to startread it →
on this pageshow

explore

questions

5

With React Navigation 7, why should a telehealth app render signed-in and signed-out screens conditionally instead of calling navigate('Home') after sign-in?

level: juniorimportance: must knowfreq 66%

answer

  1. the screen list is the gate
  2. old branch routes get dropped
  3. no SignIn left in history
  4. first new screen or initialRouteName
  5. undefined screens are unreachable

basics

~20 s

In React Navigation 7 the rendered screen list is the auth gate: when auth state flips, the old screens' routes are dropped and the first new screen shows, so no sign-in history survives and signed-out users cannot reach protected screens.

solid answer

~50 s

I keep the session token in state above the navigator and render `SignIn` and `SignUp` only while it is `null`, and `Home`, `Appointments` and `VisitRoom` only while it is set. Sign-in just updates that state; I never call `navigate('Home')`. On the next render the stack router drops every route whose screen is no longer defined, and because nothing valid is left it starts the stack on the navigator's `initialRouteName` if that screen is in the new list, otherwise on the first signed-in screen. So there is no `SignIn` entry to go back to, and while signed out the protected screens do not exist, so neither a stray `navigate` nor a deep link can open them. Navigating manually does not even work: `Home` is not defined yet, so React Navigation reports in development that the `NAVIGATE` action was not handled by any navigator.

code

tsx · 37 lines
tsx
import * as React from 'react';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { useAuth } from './auth';
import { HomeScreen, SignInScreen, SignUpScreen, VisitRoomScreen } from './screens';

type RootStackParamList = {
  SignIn: undefined;
  SignUp: undefined;
  Home: undefined;
  VisitRoom: { visitId: string };
};

const Stack = createNativeStackNavigator<RootStackParamList>();

export function RootNavigator() {
  const { userToken, isSignout } = useAuth();

  return (
    <Stack.Navigator>
      {userToken == null ? (
        <Stack.Group>
          <Stack.Screen
            name="SignIn"
            component={SignInScreen}
            options={{ animationTypeForReplace: isSignout ? 'pop' : 'push' }}
          />
          <Stack.Screen name="SignUp" component={SignUpScreen} />
        </Stack.Group>
      ) : (
        <Stack.Group>
          <Stack.Screen name="Home" component={HomeScreen} />
          <Stack.Screen name="VisitRoom" component={VisitRoomScreen} />
        </Stack.Group>
      )}
    </Stack.Navigator>
  );
}

go deeper

for a junior

Recall the rule: render signed-out screens or signed-in screens depending on the token, update the token on sign-in, and never navigate to Home yourself.

for a middle

Explain what the stack router does on the flip: drops routes whose screens vanished, starts on initialRouteName or the first new screen, so no sign-in history remains.

for a senior

Argue the security side: an unrendered screen is unreachable by navigate or a deep link, and cover sign-out from a deep stack plus screens shared by both branches.

for a principal

Frame it as one source of truth for session state that every surface flips, so navigation, token expiry and sign-out cannot disagree about who is signed in.

## The pattern: the screen list is the gate In **React Navigation 7** a navigator's routes can only point at screens that are currently rendered as its children (or listed in its static configuration). An auth flow uses this directly. The app keeps an **auth state** — usually a session token, or `null` — in React state, a context or a store that sits *above* the navigator, and the navigator's children depend on it: - While the token is `null`, only the signed-out screens (`SignIn`, `SignUp`) are rendered. - While it is set, only the signed-in screens (`Home`, `Appointments`, `VisitRoom`) are rendered. - The sign-in screen's job ends at updating the auth state. It never calls `navigate('Home')`, and the sign-out button never calls `navigate('SignIn')`. This is **conditional rendering of screens**. React Navigation's own development error for an unhandled `NAVIGATE` action includes the reminder that when you use conditional rendering, navigation happens automatically and you should not navigate manually. ## What happens when the condition flips When the token appears, the navigator re-renders with a different list of route names. Both `@react-navigation/native-stack` and `@react-navigation/stack` use the same **stack router**, which then: 1. Keeps only the routes whose screen names are still defined — the `SignIn` and `SignUp` routes are dropped. 2. If no route is left, creates a fresh one for the navigator's `initialRouteName` when that name is in the new list, or otherwise for the first screen in the list. 3. Clamps the focused index into the new, shorter list of routes. The patient therefore lands on `Home` with a one-entry history. There is no `SignIn` route underneath, so the back button or the back gesture cannot return to the sign-in form, which is exactly what a signed-in user expects. **Signing out is the same process in reverse**: every signed-in route, however deep the stack (`Home` → `Appointments` → `VisitRoom`), disappears in one step and the stack starts again on `SignIn`. ## Why navigating after sign-in is the wrong tool | Approach | What goes wrong | |---|---| | All screens always defined, `navigate('Home')` after sign-in | `SignIn` stays below `Home`; back returns to the login form | | All screens always defined, a manual `reset` in every auth handler | History is patched by hand in each handler, and protected screens still exist while signed out | | Protected screens guarded by a check inside each screen | A deep link or a stray `navigate` still mounts them, and every screen needs its own guard | | **Conditional screens** | The router rebuilds history itself, and signed-out users have no protected screen to reach | The last row carries a **security argument**, not only a UX one. When `VisitRoom` is not rendered, no action, link or bug can open it, because the navigator does not know that name. A `navigate('VisitRoom')` while signed out goes unhandled: in development React Navigation logs a `console.error` saying the action "was not handled by any navigator", and in production it is silently ignored. ## Details that make the swap feel right - **Animation direction.** A swap animates like a push by default. React Navigation's auth-flow example sets `animationTypeForReplace: isSignout ? 'pop' : 'push'` on `SignIn`, so signing out animates like going back. Both stack navigators accept the option, and it defaults to `'push'`. - **Groups.** Wrapping each branch in `Stack.Group` keeps the two lists readable and lets a branch share `screenOptions`. - **One source of truth.** The auth state lives above the navigator so the sign-in screen, the sign-out button and a token-expiry handler all flip the same value; any of them changing it is enough. - **Restoring a session.** At launch the saved token is not known yet, so the app needs a third state — restoring — and a splash, instead of defaulting to signed out and flashing the sign-in form. - **Screens in both branches.** A screen rendered regardless of auth state (a help page, a privacy policy) keeps its route across the flip; the `navigationKey` prop exists for that case. ## What this pattern does not decide Where the token is stored and how it is protected, how an OAuth sign-in redirects back into the app, and how Expo Router guards routes in a file-based project are separate subjects with their own answers. The navigation rule itself is short: render only the screens the current auth state allows, change the state, and let the router rebuild the history. An interviewer who hears "I call `navigate` after login and then fix the back button" is hearing the pattern React Navigation's documentation and error messages steer you away from.

  • Which screen does the patient see right after signing in, and can you choose it?
    The stack router drops the signed-out routes, finds nothing valid left, and creates a route for the navigator's `initialRouteName` if that screen is in the new list, otherwise for the first signed-in screen in declaration order. So you choose it either by setting `initialRouteName` on the navigator or by declaring the landing screen first in the signed-in branch.
  • What does sign-out look like in this pattern?
    The sign-out handler clears the auth state (and the stored token, which is a storage concern). On the next render every signed-in route disappears at once, even a deep `Home` → `Appointments` → `VisitRoom` stack, and the stack starts on `SignIn`. Setting `animationTypeForReplace: 'pop'` on `SignIn` for that case makes the swap animate like going back.
  • What happens to a Help screen that is rendered in both branches?
    Its route survives the flip, because its name is still defined after the condition changes. If it was focused when the patient signed out, the stack can end up holding only `Help` with no `SignIn` beneath it. Giving that screen, or a group of shared screens, a `navigationKey` that changes with the auth state makes React Navigation remove those routes too.

saying these in an interview costs you the question

  • Call navigation.navigate('Home') from the sign-in screen once the token arrives.
  • After sign-in, the back gesture should return to SignIn because it is still in the stack.
  • Hide protected screens with a token check inside each screen instead of not rendering them.
  • Conditional screens remount the whole NavigationContainer on every auth change.
  • A signed-out user can still open VisitRoom with navigate() if its screen is not rendered.
open as a page

In React Navigation 7, how do you avoid flashing the sign-in screen while a telehealth app restores a saved session token at launch?

level: middleimportance: should knowfreq 46%

basics

~20 s

Model auth as three states — restoring, signed out, signed in — and render a splash while restoring, either instead of the navigator or as its only screen, so React Navigation never mounts SignIn before the saved token has been read.

open as a page

With React Navigation 7's static configuration, how do the if hooks on screens and groups express a signed-in and signed-out split?

level: middleimportance: should knowfreq 24%

basics

~20 s

In React Navigation 7's static API, a screen or group's if property takes a hook returning a boolean; the entry renders only while it returns true, so signed-in and signed-out groups swap exactly like conditionally rendered dynamic screens.

open as a page

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%

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.

open as a page