skip to content

Screen Navigation

React Navigation 7 structures screens with stack, tab and drawer navigators, typed params, focus events and a linking config. Interviewers probe it because navigation structure is costly to change.

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

explore

questions

page 1 of 2

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 a React Navigation 7 stack, what happens to the product list screen when you push a details screen, and when does it unmount?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Pushing a details screen keeps the product list mounted underneath with its state intact; it only receives a blur event. It unmounts when its route is removed from the stack state: going back past it, replace or reset.

open as a page

In React Navigation 7, when do you use a stack navigator, bottom tabs or a drawer to organise an app's screens?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Use a stack for drill-down flows where screens are pushed and popped as history, bottom tabs for a few top-level peer sections that each keep their state, and a drawer when there are more peer destinations than fit in a tab bar.

open as a page

In React Navigation 7, what changes when a stack is nested inside each tab versus the whole tab navigator being nested inside a stack?

level: juniorimportance: must knowfreq 58%

basics

~20 s

A stack inside a tab gives that tab its own history and keeps the tab bar visible; tabs inside a stack let root-stack screens cover the tab bar. Most apps combine both: per-tab stacks inside a root stack.

open as a page

With React Navigation 7, how do you pass an order id to a delivery-tracking screen and read it inside that screen?

level: juniorimportance: must knowfreq 76%

basics

~10 s

Call navigation.navigate('Tracking', { orderId }), where the second argument is the params object, then read route.params.orderId in the Tracking screen, or call useRoute() from any component rendered inside that screen.

open as a page

With React Navigation 7, what do the linking prop's prefixes and config.screens do when a shared furniture-store product URL opens the app?

level: juniorimportance: must knowfreq 55%

basics

~20 s

In React Navigation 7, linking.prefixes are stripped from the incoming URL, and the remaining path is matched against config.screens, which maps screen names to path patterns; matched segments and query values become that screen's params.

open as a page

In React Navigation 7 bottom tabs, why does a mount useEffect show a stale cart on returning to the tab, and what fixes it?

level: middleimportance: must knowfreq 66%

basics

~20 s

Visited tabs stay mounted, so a useEffect with empty dependencies runs once and never on return. useFocusEffect runs its callback each time the screen gains focus and its cleanup on blur, so the cart refreshes on every return.

open as a page

In React Navigation 7, how do you choose between the native stack and the JS stack navigator, and what does each trade away?

level: middleimportance: must knowfreq 55%

basics

~20 s

The native stack uses react-native-screens' native navigation primitives, so transitions, header and gestures behave like the platform's own. The JS stack re-implements them in JavaScript, trading that native feel for fully customisable transitions and headers.

open as a page

With React Navigation 7, how do you open NotificationSettings, a screen in the Profile tab's nested settings stack, from the Discover tab?

level: middleimportance: must knowfreq 60%

basics

~20 s

Navigate to the screen that hosts the nested navigator and name the child in its params: navigate('Profile', { screen: 'NotificationSettings', params, initial: false }). A bare navigate('NotificationSettings') fails in v7 because actions bubble to parents, not into children.

open as a page

In a React Navigation 7 stack, how do navigate, push, replace and popTo differ, and why did navigate stop going back?

level: middleimportance: must knowfreq 62%

basics

~20 s

In React Navigation 7, navigate pushes unless that screen is focused, push adds an entry even for a duplicate, replace swaps the current entry, and popTo returns to an existing screen, the go-back that v7 removed from navigate.

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, when should a screen use useIsFocused rather than useFocusEffect or navigation.isFocused()?

level: middleimportance: should knowfreq 35%

basics

~20 s

Use useIsFocused when what the screen renders depends on focus, such as mounting a camera preview only while visible; it re-renders on each focus change. Use useFocusEffect for side effects, and never read navigation.isFocused() during render.

open as a page

In React Navigation 7's native stack, how does the presentation option change how an audiobook player screen appears, and what differs on Android?

level: middleimportance: should knowfreq 35%

basics

~20 s

presentation defaults to 'card', a normal push. 'modal' presents the screen modally, 'transparentModal' keeps the previous screen visible behind it, and iOS styles such as 'fullScreenModal' or 'pageSheet' fall back to a plain modal on Android.

open as a page

In React Navigation 7, how do a navigator's screenOptions, a group's screenOptions, a screen's options and navigation.setOptions combine for one screen?

level: middleimportance: should knowfreq 45%

basics

~20 s

They are merged per key in order: the navigator's screenOptions, then each group's screenOptions, then the screen's options, then anything set with navigation.setOptions. Later sources win, and any of them can be a function of { route, navigation, theme }.

open as a page

In React Navigation 7, what is the difference between the static API with createStaticNavigation and the dynamic API inside NavigationContainer?

level: middleimportance: should knowfreq 42%

basics

~20 s

The static API describes navigators as configuration objects and turns the root into a component with createStaticNavigation, which infers types and can generate deep-link paths. The dynamic API renders Navigator and Screen components inside NavigationContainer, trading that automation for runtime flexibility.

open as a page

In React Navigation 7 nested navigators, how does an action from a nested screen reach a parent navigator, and when do you need navigation.getParent()?

level: middleimportance: should knowfreq 40%

basics

~20 s

An action goes to the screen's own navigator first and bubbles to each parent until one handles it, so navigate('Chat') from a nested screen reaches the root stack. getParent() covers what stays local: the parent screen's options and events.

open as a page

In React Navigation 7, why do two headers appear when navigators are nested, and how does getFocusedRouteNameFromRoute help title the remaining one?

level: middleimportance: should knowfreq 45%

basics

~20 s

The parent and the nested navigator each render a header by default, so two stack up. Hide one level with headerShown: false; if the parent's header stays, getFocusedRouteNameFromRoute(route) in its options reads the focused child's name.

open as a page

With React Navigation 7, how does a screen return a result to its opener, and how do setParams, replaceParams and merge differ?

level: middleimportance: should knowfreq 50%

basics

~20 s

Return a result by navigating back with params, for example popTo('Tracking', { orderId, note }, { merge: true }). setParams shallow-merges into the current route's params, replaceParams replaces them, and merge: true keeps a target's existing params.

open as a page

In React Navigation 7, why should route params hold an order id rather than the order object or an onDelivered callback?

level: middleimportance: should knowfreq 45%

basics

~20 s

Route params live in React Navigation's navigation state, which must stay serializable for persistence, deep links and tooling. An id is small and lets the screen read current data; an object copy goes stale and a function triggers a development warning.

open as a page

With React Navigation 7, how must linking config be shaped so a product URL opens Product in a tabbed Shop stack with Catalog behind it?

level: middleimportance: should knowfreq 42%

basics

~10 s

React Navigation 7's config.screens must nest exactly like the navigators: tabs, then the Shop stack, then Product. Setting initialRouteName: 'Catalog' on the Shop entry makes a linked Product open with Catalog beneath it.

open as a page

With React Navigation 7, why does a screen-view tracker on NavigationContainer need onReady as well as onStateChange?

level: middleimportance: should knowfreq 36%

basics

~20 s

React Navigation 7 does not call onStateChange for the initial state, so a tracker using only it misses the first screen. onReady fires once when navigators are rendered, which is where the initial route is recorded.

open as a page

In React Navigation 7, what happens when a URL matches no linking path, and how do you route it to a NotFound screen?

level: middleimportance: should knowfreq 34%

basics

~20 s

An unmatched URL yields no state in React Navigation 7, so a cold start opens the default initial screen and a running app ignores the link. A screen with the path '*' catches every path that nothing more specific matches.

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

In React Navigation 7, how do you stop a user leaving a product-review screen with unsaved text, and what can still bypass that guard?

level: seniorimportance: should knowfreq 45%

basics

~10 s

Call usePreventRemove(hasUnsavedText, ({ data }) => confirm, then navigation.dispatch(data.action)). It intercepts any navigation action that would remove the screen, but not switching tabs, pushing another screen, backgrounding or killing the app.

open as a page

In a React Navigation 7 TypeScript app, how do you type a settings screen nested in the Profile tab so it can navigate to root screens?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Declare the child list inside the parent's with NavigatorScreenParams, then type the nested screen with CompositeScreenProps: its own navigator's props first, then the parent's, composed again up to the root. Static configuration infers the nested params automatically.

open as a page

In a large React Navigation 7 TypeScript app, how do you type route params so navigate, useNavigation and useRoute are all checked?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Declare a param list type mapping each screen to its params, type screens with the navigator's ScreenProps helper, register the root list through the global ReactNavigation.RootParamList interface, and with static config infer the list via StaticParamList.

open as a page

With React Navigation 7, why would you override linking.getInitialURL and linking.subscribe so a tapped push notification opens a product screen?

level: seniorimportance: should knowfreq 30%

basics

~20 s

React Navigation 7's defaults read URLs only from React Native's Linking. A push tap delivers its URL through the notification library instead, so getInitialURL must also check the launch notification and subscribe must also listen for taps.

open as a page

In React Navigation 7, how do you persist and restore navigation state with onStateChange and initialState without breaking deep links?

level: seniorimportance: should knowfreq 27%

basics

~20 s

Save the state from onStateChange and pass it back as initialState on launch, but only when no launch URL exists: in React Navigation 7, providing initialState makes the container skip deep-link handling on the initial render.

open as a page

showing 1–30 of 31