Topic 9: Navigation
📖 8 min read · 🎯 advanced · 🧭 Prerequisites: using-libraries-in-php, hooks
Why this matters
Up until now, your app has been a single screen — whatever you build just sits there. But real apps let users move around: tap a button and go to a new screen, hit back and return, open a side menu. That movement is what React Navigation handles. In this lesson, we're covering the three layouts you'll see in almost every production app — stack navigation, bottom tabs, and the side drawer. By the end, your app will feel like a real app, not just a prototype.
What You'll Learn
- Install and configure React Navigation and its peer dependencies, including
react-native-gesture-handler - Build stack navigation with
@react-navigation/stackso screens slide in with a back button - Implement bottom tab navigation with
@react-navigation/bottom-tabsfor tab-bar switching - Set up drawer navigation with
@react-navigation/drawerfor a swipeable side menu
The Analogy
Think of React Navigation as the road network of Vizag. Stack navigation is a one-way highway — you merge onto it, move forward through screens, and hit the back button to exit the last on-ramp. Bottom tab navigation is like a city roundabout with four exits, each leading to a distinct district you can jump between freely. Drawer navigation is the hidden side-street map that slides out from the edge of town when you need it — always there, never in the way. NavigationContainer is city hall: nothing moves until it's open for business.
Chapter 1: Setting Up React Navigation
Before any screen can talk to another, the library stack must be in place.
Step 1: Install React Navigation and dependencies
npm install @react-navigation/native
npm install @react-navigation/stack
npm install @react-navigation/bottom-tabs
npm install @react-navigation/drawer
npm install react-native-screens react-native-safe-area-context
@react-navigation/native— the core container and hooks@react-navigation/stack— push/pop screen navigator@react-navigation/bottom-tabs— tab bar navigator@react-navigation/drawer— side-drawer navigatorreact-native-screens— native screen primitives for performancereact-native-safe-area-context— insets for notches and home bars
Step 2: Install and configure React Native Gesture Handler
React Navigation relies on react-native-gesture-handler for swipe-back and drawer gestures.
npm install react-native-gesture-handler
For React Native versions below 0.60, manually link the library:
react-native link react-native-gesture-handler
Then update MainActivity.java so the root view is gesture-aware:
android/app/src/main/java/com/yourappname/MainActivity.java
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
public class MainActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
return "yourappname";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected ReactRootView createRootView() {
return new RNGestureHandlerEnabledRootView(MainActivity.this);
}
};
}
}
Wrapping the root view with RNGestureHandlerEnabledRootView ensures gesture events propagate correctly on Android.
Chapter 2: Setting Up Stack Navigation
Stack navigation mimics the browser's history stack — push a screen on, pop it off. A header with a back button appears automatically.
Step 1: Create the screen components
HomeScreen.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
const HomeScreen = ({ navigation }) => {
return (
<View style={styles.container}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default HomeScreen;
DetailsScreen.js
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const DetailsScreen = () => {
return (
<View style={styles.container}>
<Text>Details Screen</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default DetailsScreen;
navigation.navigate('Details') pushes DetailsScreen onto the stack. The back button in the header calls navigation.goBack() implicitly.
Step 2: Configure the Stack Navigator in App.js
App.js
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from './HomeScreen';
import DetailsScreen from './DetailsScreen';
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
initialRouteName="Home" sets which screen mounts first. NavigationContainer manages the navigation tree state and must wrap everything.
flowchart LR
NC[NavigationContainer] --> SN[Stack.Navigator]
SN --> HS[HomeScreen\n\"Home\"]
SN --> DS[DetailsScreen\n\"Details\"]
HS -- navigate('Details') --> DS
DS -- back button --> HS
Chapter 3: Implementing Bottom Tab Navigation
Bottom tab navigation gives users instant access to top-level sections — no back button required.
Step 1: Create additional screen components
SettingsScreen.js
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const SettingsScreen = () => {
return (
<View style={styles.container}>
<Text>Settings Screen</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default SettingsScreen;
ProfileScreen.js
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const ProfileScreen = () => {
return (
<View style={styles.container}>
<Text>Profile Screen</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default ProfileScreen;
Step 2: Configure the Bottom Tab Navigator
App.js
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import HomeScreen from './HomeScreen';
import DetailsScreen from './DetailsScreen';
import SettingsScreen from './SettingsScreen';
import ProfileScreen from './ProfileScreen';
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Details" component={DetailsScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
createBottomTabNavigator() renders a persistent tab bar at the bottom. Each Tab.Screen entry becomes one tab. Tapping a tab switches the active screen without pushing it onto a stack — state for each tab is preserved independently.
Chapter 4: Implementing Drawer Navigation
A drawer navigator slides a menu panel in from the side, giving access to all top-level routes without occupying permanent screen real estate.
Step 1: Configure the Drawer Navigator
App.js
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createDrawerNavigator } from '@react-navigation/drawer';
import HomeScreen from './HomeScreen';
import DetailsScreen from './DetailsScreen';
import SettingsScreen from './SettingsScreen';
import ProfileScreen from './ProfileScreen';
const Drawer = createDrawerNavigator();
export default function App() {
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Details" component={DetailsScreen} />
<Drawer.Screen name="Settings" component={SettingsScreen} />
<Drawer.Screen name="Profile" component={ProfileScreen} />
</Drawer.Navigator>
</NavigationContainer>
);
}
createDrawerNavigator() from @react-navigation/drawer produces a side panel that users can open by swiping from the left edge or tapping the hamburger icon that appears in the header. initialRouteName="Home" dictates the default landing screen when the app opens.
flowchart TD
NC[NavigationContainer] --> DN[Drawer.Navigator\ninitialRoute: Home]
DN --> H[Home]
DN --> D[Details]
DN --> S[Settings]
DN --> P[Profile]
style DN fill:#2d6a4f,color:#fff
Choosing the right navigator
| Navigator | Package | Best for |
|---|---|---|
| Stack | @react-navigation/stack | Linear flows, detail views |
| Bottom Tabs | @react-navigation/bottom-tabs | 3–5 top-level sections |
| Drawer | @react-navigation/drawer | Many routes, infrequent access |
Navigators can be nested — for example, a bottom tab navigator where one tab contains its own stack navigator. This is the most common pattern in production apps.
🧪 Try It Yourself
Task: Build a mini app that combines a bottom tab navigator with a nested stack inside the "Home" tab.
- Set up the two-tab shell:
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createStackNavigator } from '@react-navigation/stack';
import { View, Text, Button } from 'react-native';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const HomeScreen = ({ navigation }) => (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home</Text>
<Button title="Go to Details" onPress={() => navigation.navigate('Details')} />
</View>
);
const DetailsScreen = () => (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details</Text>
</View>
);
const SettingsScreen = () => (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Settings</Text>
</View>
);
function HomeStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStack} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
Success criterion: You see two tabs at the bottom. Tapping "Home" shows a "Go to Details" button. Pressing it pushes the Details screen with a back arrow in the header. The Settings tab stays accessible throughout. No crashes, no missing back button.
🔍 Checkpoint Quiz
Q1. Why must NavigationContainer wrap the entire navigator tree?
A) It provides the gesture handler context
B) It manages navigation state and links deep-link URLs to the correct screen
C) It renders the tab bar UI
D) It is only needed for drawer navigation
Q2. Given this snippet, what happens when the button is pressed?
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
A) The Details screen replaces the current screen permanently
B) The Details screen is pushed onto the stack; a back button appears in the header
C) The app crashes because navigate is not a valid method
D) The drawer opens to show the Details entry
Q3. Which of the following code fragments correctly configures a drawer navigator with HomeScreen as the initial route?
// Option A
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
</Drawer.Navigator>
// Option B
<Drawer.Navigator defaultScreen="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
</Drawer.Navigator>
A) Option A
B) Option B
C) Both are equivalent
D) Neither; drawers do not support an initial route
Q4. You have five sections in your app: Feed, Explore, Notifications, Messages, and Profile. Which navigator is the most appropriate top-level choice, and why?
A1. B — NavigationContainer holds the navigation state tree and handles deep linking. Without it, navigators have nowhere to store their state and the app will throw an error on mount.
A2. B — navigation.navigate('Details') pushes DetailsScreen onto the stack. React Navigation automatically adds a back button to the header because there is now a previous screen in the history.
A3. A — The correct prop is initialRouteName, not defaultScreen. Option B would be silently ignored and the first declared screen would be shown.
A4. Bottom Tab Navigator (@react-navigation/bottom-tabs) — five peer sections that users switch between freely are the canonical use case for tabs. A stack would force linear flow; a drawer buries sections behind a gesture instead of surfacing them permanently.
🪞 Recap
- Install
@react-navigation/native, navigator packages,react-native-screens,react-native-safe-area-context, andreact-native-gesture-handlerbefore writing any navigation code. - Wrap every navigator tree in
<NavigationContainer>— it owns the navigation state. - Stack navigation (
createStackNavigator) handles linear screen flows and provides a built-in header with a back button. - Bottom tab navigation (
createBottomTabNavigator) renders a persistent tab bar for switching between peer-level screens. - Drawer navigation (
createDrawerNavigator) provides a swipeable side menu, best for routes that are accessed less frequently. - Navigators can be nested — a common pattern is a bottom tab navigator containing stacks inside each tab.
📚 Further Reading
- React Navigation official docs — the source of truth for all navigator APIs and options
- Nesting navigators guide — detailed patterns for combining stack, tab, and drawer navigators
- react-native-gesture-handler README — setup details and gotchas for both Android and iOS
- ⬅️ Previous: Hooks
- ➡️ Next: Socket.IO — The Front End and a Chat App