Lesson 10

Nesting Navigators

Real apps rarely use a single navigator. A typical setup has a tab bar for top-level sections, with each tab containing its own stack for drill-down navigation. You might wrap the whole thing in a drawer for secondary navigation. React Navigation supports this by letting you nest navigators — use one navigator as a screen inside another.

Stack Inside Tabs

The most common pattern. Each tab has its own stack so users can navigate deeper within a section without losing their place in other tabs:

import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { View, Text, Button, StyleSheet } from 'react-native';

const Tab = createBottomTabNavigator();
const HomeStack = createNativeStackNavigator();
const SettingsStack = createNativeStackNavigator();

function HomeScreen({ navigation }) {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Home</Text>
      <Button title="View Details" onPress={() => navigation.navigate('HomeDetails')} />
    </View>
  );
}

function HomeDetailsScreen() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Home Details</Text>
    </View>
  );
}

function SettingsScreen({ navigation }) {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Settings</Text>
      <Button title="Edit Profile" onPress={() => navigation.navigate('EditProfile')} />
    </View>
  );
}

function EditProfileScreen() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Edit Profile</Text>
    </View>
  );
}

function HomeStackNavigator() {
  return (
    <HomeStack.Navigator>
      <HomeStack.Screen name="HomeMain" component={HomeScreen} options={{ title: 'Home' }} />
      <HomeStack.Screen name="HomeDetails" component={HomeDetailsScreen} options={{ title: 'Details' }} />
    </HomeStack.Navigator>
  );
}

function SettingsStackNavigator() {
  return (
    <SettingsStack.Navigator>
      <SettingsStack.Screen name="SettingsMain" component={SettingsScreen} options={{ title: 'Settings' }} />
      <SettingsStack.Screen name="EditProfile" component={EditProfileScreen} options={{ title: 'Edit Profile' }} />
    </SettingsStack.Navigator>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
  title: { fontSize: 24, marginBottom: 16 },
});

export default function App() {
  return (
    <NavigationContainer>
      <Tab.Navigator screenOptions={{ headerShown: false }}>
        <Tab.Screen name="Home" component={HomeStackNavigator} />
        <Tab.Screen name="Settings" component={SettingsStackNavigator} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

Notice headerShown: false on the tab navigator. Without this, you'd see two headers — one from the tab navigator and one from the stack. Setting it to false on the outer navigator lets the inner stack handle the header.

Tabs Inside a Drawer

Wrap the tab navigator in a drawer for secondary navigation:

import { createDrawerNavigator } from '@react-navigation/drawer';

const Drawer = createDrawerNavigator();

function MainTabs() {
  return (
    <Tab.Navigator screenOptions={{ headerShown: false }}>
      <Tab.Screen name="Home" component={HomeStackNavigator} />
      <Tab.Screen name="Settings" component={SettingsStackNavigator} />
    </Tab.Navigator>
  );
}

export default function App() {
  return (
    <NavigationContainer>
      <Drawer.Navigator>
        <Drawer.Screen name="Main" component={MainTabs} />
        <Drawer.Screen name="About" component={AboutScreen} />
      </Drawer.Navigator>
    </NavigationContainer>
  );
}

The drawer contains the tab navigator as one of its screens. Opening the drawer slides over the tabs. This is a common pattern — the drawer provides access to less-frequently-used sections while the tabs handle the main app flow.

How Nesting Affects Behavior

A few things to know when nesting:

  • Each navigator has its own navigation state. The tab navigator tracks which tab is active. Each stack navigator tracks its own screen history. They're independent.
  • Navigating between nested navigators uses the full screen name. From inside the Home stack, navigation.navigate('Settings') switches to the Settings tab.
  • The back button pops the innermost stack first. If you're on HomeDetails inside the Home tab, pressing back goes to HomeMain. It won't switch tabs.
  • Tab persistence — when you switch tabs and come back, the stack state is preserved. If you were on HomeDetails, switching to Settings and back still shows HomeDetails.

The Navigation Hierarchy

Think of nesting as a tree:

Drawer
  └── Tabs
        ├── Home Stack
        │     ├── HomeMain
        │     └── HomeDetails
        └── Settings Stack
              ├── SettingsMain
              └── EditProfile

Each node manages its own navigation state. Calls to navigate search from the current position outward through the tree until they find a matching screen name.

Once nesting clicks, the main challenge becomes reaching across navigator boundaries — accessing a parent's methods, or navigating into a sibling's stack. That's next.