skip to content

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%

answer

  1. name the parent screen first
  2. screen and params in the params
  3. actions bubble up, not down
  4. initial: false keeps the first screen
  5. deeper nesting repeats the shape

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.

solid answer

~40 s

In React Navigation 7 you address a nested screen through the screen that renders its navigator: from Discover, `navigation.navigate('Profile', { screen: 'NotificationSettings', params: { section: 'matches' } })`. The tab navigator handles `Profile`, and the nested settings stack reads `screen` and `params` to open the child. A bare `navigate('NotificationSettings')` is not handled: an unhandled action bubbles up to parent navigators, but it is no longer passed down into children, so development logs that the action was not handled by any navigator. If the Profile stack has not been rendered yet, it is created with only the target screen, so there is no ProfileHome to go back to; adding `initial: false` makes it start from its initial route and push the target on top. Deeper levels nest the same `{ screen, params }` shape again.

code

tsx · 32 lines
tsx
import type { NavigatorScreenParams } from '@react-navigation/native';
import type { BottomTabScreenProps } from '@react-navigation/bottom-tabs';
import { Button } from 'react-native';

type ProfileStackParamList = {
  ProfileHome: undefined;
  Settings: undefined;
  NotificationSettings: { section: 'matches' | 'messages' };
};

type MainTabParamList = {
  Discover: undefined;
  Matches: undefined;
  Profile: NavigatorScreenParams<ProfileStackParamList> | undefined;
};

export function DiscoverScreen({
  navigation,
}: BottomTabScreenProps<MainTabParamList, 'Discover'>) {
  return (
    <Button
      title="Match notification settings"
      onPress={() =>
        navigation.navigate('Profile', {
          screen: 'NotificationSettings',
          params: { section: 'matches' },
          initial: false,
        })
      }
    />
  );
}

go deeper

for a junior

Recall the shape: navigate to the screen that hosts the nested navigator and pass screen and params for the child inside its params.

for a middle

Explain why a bare child name fails in v7, how actions bubble up but not down, and what initial: false changes for an unrendered nested stack.

for a senior

Show you build navigation helpers and typed nested params so feature code never relies on deprecated child navigation and back behaviour stays predictable.

for a principal

Keep the navigation tree shallow enough that cross-feature routes stay short, and own a single typed map of entry points instead of ad-hoc nested calls.

## The navigation tree in a dating app Consider a dating app built with **React Navigation 7**: - a root **stack** with `MainTabs`, a full-screen `Chat` and a `Paywall` modal; - `MainTabs` is a **bottom tab navigator** with `Discover`, `Matches` and `Profile`; - `Profile` renders a **nested stack**: `ProfileHome`, `Settings`, `NotificationSettings`. Each navigator owns its own routes and state. The tab navigator knows the name `Profile` but not `NotificationSettings`; only the nested stack knows that name. ## Addressing a nested screen To reach a screen inside a nested navigator, you navigate to the **screen that renders that navigator** and describe the child in the params: ```tsx navigation.navigate('Profile', { screen: 'NotificationSettings', params: { section: 'matches' }, }); ``` The nested params object accepts these keys in the pinned types: | Key | Meaning | |---|---| | `screen` | the child screen to open | | `params` | that child's own params | | `initial` | `false` keeps the nested navigator's initial route underneath | | `merge` | merge the child's params into its existing params | | `pop` | go back to an existing child route instead of pushing | | `state` | a full nested state, used instead of `screen` | For a deeper tree the shape repeats: from the root stack you would write `navigate('MainTabs', { screen: 'Profile', params: { screen: 'NotificationSettings' } })`. ## Why the short form fails in v7 A navigation action is first offered to the navigator of the screen that dispatched it. If that navigator has no route with the name, the action **bubbles up** to the parent, then the grandparent, up to the root. In React Navigation 7 it is **not** passed **down** into child navigators by default. So `navigate('NotificationSettings')` from `Discover`: 1. the tab navigator has no screen with that name; 2. the root stack has none either; 3. nobody handles it, and in development the container logs that the action was not handled by any navigator, with a pointer to the nested-navigation docs. The container prop `navigationInChildEnabled` restores the older behaviour of trying child navigators, but it is marked deprecated, only works when the child navigator is already mounted, and is slated for removal in the next major release. The explicit nested form is the supported API. ## The initial route problem What happens next depends on whether the nested stack has been rendered: - **Already rendered** (the user visited Profile before): the stack receives a `navigate` to `NotificationSettings` and pushes it on top of what it has. - **Not rendered yet** (tabs mount lazily on first visit): the stack is **initialised with only the target screen**. `NotificationSettings` appears, but there is no `ProfileHome` beneath it, so the back button in that stack has nowhere to go. Passing **`initial: false`** changes the second case: the stack first initialises normally from its `initialRouteName`, then navigates to the target, so `ProfileHome` sits below `NotificationSettings` and back works as users expect. ## Params, merge and pop in nested navigation Params for the child go in the inner `params`, never at the outer level: `navigate('Profile', { section: 'matches' })` would give the section to the `Profile` route of the tab navigator, where the settings screen never sees it. `merge` and `pop` behave as they do on a flat `navigate`: `merge: true` keeps the child's existing params, and `pop: true` returns to an existing `NotificationSettings` in the nested stack rather than pushing another. ## Practical guidance - Use the nested form everywhere in feature code; do not rely on deprecated child navigation. - Add `initial: false` whenever the target is not the nested navigator's first screen and back should lead into that navigator. - Do not give a nested screen the same name as the screen that hosts its navigator, such as a `Profile` tab whose stack starts with a screen also called `Profile`. React Navigation warns in development that it found screens with the same name nested inside one another, because a navigation to that name becomes ambiguous. - Remember v7's stack semantics apply inside the nested stack: if `NotificationSettings` is already open there, a second nested `navigate` pushes another copy unless you pass `pop: true` or configure `getId`. - With TypeScript, declare the tab's param as `NavigatorScreenParams<ProfileStackParamList>`, so the compiler checks the `screen` name and its params.

  • After navigating from Discover to NotificationSettings, the back button in the Profile stack does nothing; why?
    The Profile stack had not been rendered, so it was initialised with only `NotificationSettings`, and there is no route below it. Pass `initial: false` in the nested params; the stack then starts from its initial `ProfileHome` and pushes the target on top.
  • How would you reach NotificationSettings from the root-level Chat screen?
    Chat lives in the root stack, so the path starts there: `navigate('MainTabs', { screen: 'Profile', params: { screen: 'NotificationSettings', params: { section: 'messages' }, initial: false } })`. Each level names the screen that renders the next navigator.

saying these in an interview costs you the question

  • navigate('NotificationSettings') from any tab finds the nested screen automatically.
  • The child's params go at the outer level next to screen.
  • Navigating into an unvisited nested stack always keeps its first screen underneath.
  • navigationInChildEnabled is the recommended way to reach nested screens in v7.
  • A nested navigator's screens are registered in the parent navigator too.