Implementing Stack Navigation in React Native
Course: React Native Training
Topic: Implementing Stack Navigation
Course Link: React Native Training Course
Demo Link: Register for a Course Demo
JustAcademy Website: Visit JustAcademy
1. Introduction to Stack Navigation
Stack navigation is a common navigation pattern used in React Native applications to move between multiple screens. It allows a new screen to open above the current screen while preserving navigation history. Users can return to earlier screens using the header back button, Android back navigation, or supported swipe gestures.
For example, an online shopping application may start with a Home screen. When the user selects a product, the Product Details screen opens. Selecting Buy Now may open a Checkout screen. The user can then move backward through these screens without restarting the application.
React Native does not provide a complete built-in navigation system for managing application screens. React Navigation is a widely used library that supports stack navigation, tab navigation, drawer navigation, and other navigation patterns.
The @react-navigation/native-stack package provides native stack navigation using platform navigation primitives. It is suitable for many standard mobile navigation flows.
Official documentation: React Navigation Native Stack Navigator.
2. Learning Objectives
After completing this topic, learners should be able to:
- Understand the meaning and purpose of stack navigation.
- Explain the Last In, First Out (LIFO) concept.
- Install React Navigation dependencies.
- Create a native stack navigator using
createNativeStackNavigator().
- Register screens using
Stack.Screen.
- Navigate between screens using navigation methods.
- Pass and receive parameters between screens.
- Customize screen headers, titles, and navigation options.
- Understand the difference between
navigate(), push(), replace(), and goBack().
- Identify and troubleshoot common navigation errors.
- Build a multi-screen application using stack navigation.
3. What Is Stack Navigation?
Stack navigation organizes screens as a sequence. When a new screen opens, it is placed above the existing screen in the navigation stack. The newest screen is usually the visible screen, and the user can return to earlier screens through back navigation.
This behavior follows the Last In, First Out principle. The last screen added to the stack is generally the first one removed when the user navigates backward.
3.1 Example of a Navigation Stack
Home Screen
|
| Open Product Details
v
Product Details Screen
|
| Open Checkout
v
Checkout Screen
| User Action |
Stack from Bottom to Top |
Visible Screen |
| Launch application |
Home |
Home |
| Open a product |
Home → ProductDetails |
Product Details |
| Open checkout |
Home → ProductDetails → Checkout |
Checkout |
| Go back once |
Home → ProductDetails |
Product Details |
| Go back again |
Home |
Home |
3.2 Real-World Applications
- E-commerce: Home → Product Details → Cart → Checkout.
- Social media: Feed → User Profile → Post Details.
- Education: Course List → Course Details → Lesson Details.
- Banking: Dashboard → Account Details → Transaction Details.
- Travel: Search Results → Booking Details → Payment.
- Settings: Settings List → Privacy Settings → Permission Details.
4. Main Components of Stack Navigation
| Component or Method |
Purpose |
NavigationContainer |
Manages the navigation tree and navigation state. |
createNativeStackNavigator() |
Creates a native stack navigator. |
Stack.Navigator |
Defines the stack and its shared configuration. |
Stack.Screen |
Registers an individual screen and its route name. |
navigation.navigate() |
Navigates to a registered route. |
navigation.goBack() |
Requests navigation to the previous screen when possible. |
navigation.push() |
Pushes a new route instance onto the stack. |
navigation.replace() |
Replaces the current route with another route. |
navigation.pop() |
Removes the top route from the stack. |
route.params |
Provides access to parameters passed to a screen. |
screenOptions |
Configures options shared by multiple screens. |
5. Installing React Navigation
Before implementing stack navigation, install React Navigation and its required dependencies. The commands depend on whether the project uses Expo or React Native Community CLI.
5.1 Installation for an Expo Project
Open the terminal in the project directory and run:
npm install @react-navigation/native @react-navigation/native-stack
npx expo install react-native-screens react-native-safe-area-context
5.2 Installation for a React Native Community CLI Project
Run the following commands in the project directory:
npm install @react-navigation/native @react-navigation/native-stack
npm install react-native-screens react-native-safe-area-context
For an iOS Community CLI project, install CocoaPods dependencies when required:
npx pod-install ios
Follow the official setup instructions for the versions used in your project: React Navigation Getting Started.
5.3 Purpose of the Dependencies
@react-navigation/native provides core navigation functionality.
@react-navigation/native-stack provides native stack navigation.
react-native-screens supports native screen integration.
react-native-safe-area-context helps manage safe areas around notches and system interface elements.
Important: Follow the installation process appropriate for your project. If native dependencies change in a Community CLI project, you may need to rebuild the native application.
6. Creating Your First Stack Navigator
To implement stack navigation, create screen components and register them in a stack navigator. This example uses Home and Profile screens.
6.1 Create the Home Screen
// HomeScreen.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
export default function HomeScreen({ navigation }) {
return (
<View style={styles.container}>
<Text style={styles.heading}>Home Screen</Text>
<Text>Welcome to the application.</Text>
<Button
title="Open Profile"
onPress={() => navigation.navigate('Profile')}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 20,
},
heading: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 12,
},
});
The Home screen displays a button. When the user presses it, navigation.navigate('Profile') requests navigation to the route named Profile.
6.2 Create the Profile Screen
// ProfileScreen.js
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
export default function ProfileScreen() {
return (
<View style={styles.container}>
<Text style={styles.heading}>Profile Screen</Text>
<Text>This is the user's profile page.</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 20,
},
heading: {
fontSize: 24,
fontWeight: 'bold',
},
});
6.3 Configure the Navigator in App.js
// App.js
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import HomeScreen from './HomeScreen';
import ProfileScreen from './ProfileScreen';
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Home' }}
/>
<Stack.Screen
name="Profile"
component={ProfileScreen}
options={{ title: 'My Profile' }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
6.4 Understanding the Configuration
NavigationContainer wraps the root navigation system.
createNativeStackNavigator() creates a stack navigator.
Stack.Navigator contains the routes and shared stack settings.
initialRouteName="Home" sets Home as the initial route.
Stack.Screen registers a screen.
name defines the route name used by navigation methods.
component specifies the React component rendered for the route.
options configures properties such as the header title.
When the application starts, the Home screen appears. Pressing Open Profile opens the Profile screen. The native stack header normally displays a back button when the stack contains a previous screen.
7. Understanding Navigation Methods
7.1 navigation.navigate()
The navigate() method navigates to a registered route. It is suitable for ordinary screen-to-screen navigation.
navigation.navigate('Profile');
Use the exact route name registered in the navigator. If the route is not available in the current navigation context, the application may report that it cannot handle the action.
7.2 navigation.goBack()
The goBack() method requests a return to the previous screen when one exists.
navigation.goBack();
A custom Back button can be implemented as follows:
<Button
title="Go Back"
onPress={() => navigation.goBack()}
/>
Use a custom back button when the design requires it. Otherwise, the native stack header usually supplies its own back button automatically when appropriate.
7.3 navigation.push()
The push() method adds a new route instance to the top of the stack. It is useful when users need to open another instance of the same screen, such as viewing different products in sequence.
navigation.push('ProductDetails', {
productId: 202,
});
For example, the stack may contain Home → ProductDetails (ID 101) → ProductDetails (ID 202). Going back returns to the earlier product details screen.
7.4 navigation.replace()
The replace() method replaces the current route with another route instead of keeping the current route as the immediate previous screen.
navigation.replace('Dashboard');
This can be useful after onboarding or after a completed flow where returning to the previous screen is not desired. Authentication flows require careful design so that restricted screens cannot be reached through an unintended navigation history.
7.5 navigation.pop()
The pop() method removes the top route from the stack. An optional count can be used to pop multiple screens.
navigation.pop();
navigation.pop(2);
Use stack-specific methods when you need to control stack history explicitly. Check the documentation for the version of React Navigation installed in your project.
8. Passing Parameters Between Screens
Applications often need to send information from one screen to another. For example, a product list may pass a product ID and name to a Product Details screen.
8.1 Sending Parameters
navigation.navigate('ProductDetails', {
productId: 101,
productName: 'Wireless Headphones',
});
8.2 Reading Parameters
// ProductDetailsScreen.js
import React from 'react';
import { View, Text } from 'react-native';
export default function ProductDetailsScreen({ route }) {
const { productId, productName } = route.params ?? {};
return (
<View>
<Text>Product ID: {productId ?? 'Not provided'}</Text>
<Text>Product Name: {productName ?? 'Unknown product'}</Text>
</View>
);
}
8.3 Register the Destination Screen
<Stack.Screen
name="ProductDetails"
component={ProductDetailsScreen}
options={{ title: 'Product Details' }}
/>
The destination route must be registered in the relevant navigator. Parameters should generally be serializable values, such as strings, numbers, booleans, arrays, or plain objects. Avoid passing functions or complex non-serializable objects unless there is a specific reason.
8.4 Why Route Parameters Are Useful
- They identify the item that should be displayed.
- They reduce the need to duplicate simple data across screens.
- They make screen navigation more flexible.
- They help a destination load the correct record from an API or database.
For large or frequently changing data, passing a stable identifier and fetching the latest record on the destination screen is often preferable to passing an entire object.
9. Configuring the Initial Route
The initialRouteName option specifies which registered screen should appear when the navigator initializes.
<Stack.Navigator initialRouteName="Dashboard">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Dashboard" component={DashboardScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
In this example, Dashboard is the initial route. Ensure that the route name exactly matches a screen registered in the navigator.
10. Customizing Stack Headers
React Navigation provides options for configuring screen titles, header colors, title styles, shadows, and header visibility.
10.1 Setting a Screen Title
<Stack.Screen
name="Profile"
component={ProfileScreen}
options={{ title: 'User Profile' }}
/>
10.2 Setting Shared Header Styles
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#2563eb',
},
headerTintColor: '#ffffff',
headerTitleStyle: {
fontWeight: 'bold',
},
headerShadowVisible: false,
}}
>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
The screenOptions property applies shared settings to the screens in a navigator. A screen can define its own options to override a shared setting.
10.3 Hiding the Header
<Stack.Screen
name="Welcome"
component={WelcomeScreen}
options={{ headerShown: false }}
/>
Hiding the header can be useful for onboarding pages, splash screens, or custom full-screen experiences. Plan an alternative way to return or exit the screen if the normal header back button is hidden.
10.4 Setting a Back Button Title
<Stack.Screen
name="Settings"
component={SettingsScreen}
options={{
title: 'App Settings',
headerBackTitle: 'Back',
}}
/>
Header options can differ by platform and library version. Review the supported options in the Native Stack Navigator documentation.
11. Complete Example: Home, Product Details, and Cart
The following example combines stack navigation, route parameters, and multiple screens into a small shopping flow.
// App.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
const Stack = createNativeStackNavigator();
function HomeScreen({ navigation }) {
return (
<View style={styles.container}>
<Text style={styles.heading}>Shop Home</Text>
<Text>Discover products in our store.</Text>
<Button
title="View Headphones"
onPress={() =>
navigation.navigate('ProductDetails', {
productId: 101,
productName: 'Wireless Headphones',
})
}
/>
</View>
);
}
function ProductDetailsScreen({ navigation, route }) {
const { productId, productName } = route.params ?? {};
return (
<View style={styles.container}>
<Text style={styles.heading}>Product Details</Text>
<Text>ID: {productId ?? 'Not available'}</Text>
<Text>Name: {productName ?? 'Unknown product'}</Text>
<Button
title="Add to Cart"
onPress={() =>
navigation.navigate('Cart', {
productId,
productName,
})
}
/>
</View>
);
}
function CartScreen({ route }) {
const { productName } = route.params ?? {};
return (
<View style={styles.container}>
<Text style={styles.heading}>Shopping Cart</Text>
<Text>
Selected product: {productName ?? 'No product selected'}
</Text>
</View>
);
}
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Shop Home' }}
/>
<Stack.Screen
name="ProductDetails"
component={ProductDetailsScreen}
options={{ title: 'Product Details' }}
/>
<Stack.Screen
name="Cart"
component={CartScreen}
options={{ title: 'Shopping Cart' }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 20,
gap: 12,
},
heading: {
fontSize: 23,
fontWeight: 'bold',
},
});
11.1 How the Example Works
- The application starts on the Home screen.
- The user selects View Headphones.
- The Product Details screen opens with the product ID and name.
- The user selects Add to Cart.
- The Cart screen opens with the selected product name.
- The user can return to previous screens through the header back button or device navigation.
12. Understanding Screen State and Focus
Opening another screen does not necessarily destroy the previous screen. A screen can remain mounted in the stack while another screen is displayed above it.
For example, a user may enter search text on the Home screen, open Product Details, and then return to Home. Depending on the component and navigator configuration, the previous screen's local state may still be present.
12.1 Refreshing Data When a Screen Becomes Active
If a screen needs to refresh information whenever it becomes active, use focus-aware tools such as useFocusEffect when appropriate.
import React, { useCallback } from 'react';
import { Text, View } from 'react-native';
import { useFocusEffect } from '@react-navigation/native';
export default function OrdersScreen() {
useFocusEffect(
useCallback(() => {
// Refresh orders when this screen becomes focused.
// Add data-fetching logic here.
}, [])
);
return (
<View>
<Text>My Orders</Text>
</View>
);
}
In production applications, include loading indicators, error handling, and protection against outdated requests where necessary. Do not assume that navigating back always remounts the previous screen.
13. Stack Navigation Compared with Other Navigation Types
| Navigation Type |
How It Works |
Common Use Cases |
| Stack Navigation |
Places screens one above another and maintains screen history. |
Details pages, forms, checkout, settings. |
| Bottom Tab Navigation |
Switches between primary application sections. |
Home, Search, Notifications, Profile. |
| Drawer Navigation |
Displays a side menu containing navigation destinations. |
Applications with many sections. |
| Modal Presentation |
Displays temporary content as an overlay or sheet. |
Filters, confirmations, quick forms. |
These patterns can be combined. For example, an application can use bottom tabs for its main sections and a separate stack inside each tab to manage details screens.
14. Common Errors and Troubleshooting
| Problem |
Possible Cause |
Solution |
| Route not found |
The route name is misspelled or not registered in the current navigator. |
Verify the route name and register the destination screen. |
| NavigationContainer error |
The navigation container is configured incorrectly or duplicated unnecessarily. |
Use one root NavigationContainer for the app unless an advanced setup specifically requires otherwise. |
| Module not found |
A dependency is missing or the bundler needs restarting. |
Install the required packages and restart the development server. |
| Native dependency error |
Native modules or CocoaPods dependencies are not installed correctly. |
Follow platform setup instructions and rebuild the application if required. |
| Parameters are undefined |
No parameters were passed or the destination expects different property names. |
Check the navigation call and safely read route.params. |
| Back navigation behaves unexpectedly |
The screen is the initial route or the stack history differs from expectations. |
Review the navigation flow and choose the appropriate navigation method. |
| Header is missing |
headerShown is false or the header has been customized. |
Review the screen options and custom header configuration. |
| Data does not refresh |
The previous screen remains mounted when the user returns. |
Use focus-aware refresh logic where necessary. |
14.1 Debugging Checklist
- Confirm that all required packages are installed.
- Check Metro and terminal output for dependency errors.
- Verify file names, component imports, and file paths.
- Ensure destination screens are registered in the correct navigator.
- Check route names for spelling and capitalization differences.
- Confirm that the navigation container wraps the root navigator correctly.
- Verify that route parameters use the expected property names.
- Rebuild the native application if a native dependency change requires it.
15. Best Practices for Stack Navigation
- Use descriptive route names: Names such as
ProductDetails and OrderSummary make navigation code easier to understand.
- Organize navigator configuration: In larger applications, keep navigator definitions separate from screen components.
- Choose the appropriate navigator: Use native stack when platform-native navigation behavior is suitable for the project.
- Pass only necessary parameters: Prefer stable identifiers when a screen can load its own data.
- Handle missing parameters: Do not assume every route receives valid data.
- Plan back navigation: Make sure users can return naturally without entering confusing navigation loops.
- Use replace carefully: Replacing a route changes stack history and may remove the expected back destination.
- Set clear screen titles: Helpful headers allow users to understand their current location.
- Refresh data deliberately: Use focus-aware logic rather than relying on component remounting.
- Test on devices and simulators: Check Android back behavior, iOS gestures, and different screen sizes.
- Protect sensitive flows: Authentication and payment screens need deliberate navigation and access-control design.
- Keep parameters serializable: This helps with debugging, navigation state persistence, and deep-link compatibility.
16. Practical Exercises
Exercise 1: Create a Two-Screen Application
- Create HomeScreen and AboutScreen components.
- Register both screens in a native stack navigator.
- Add a button on HomeScreen to open AboutScreen.
- Confirm that back navigation returns to HomeScreen.
Exercise 2: Pass User Information
- Create a UserDetails screen.
- Pass a user's name and ID through route parameters.
- Display those values using
route.params.
- Handle cases where parameters are missing.
Exercise 3: Customize Headers
- Set a different title for each registered screen.
- Change the header background and text color.
- Hide the header on one screen.
- Check how users navigate back when the header is hidden.
Exercise 4: Build a Product Flow
Create an application with Home, Product Details, Cart, and Order Confirmation screens. Pass a product ID between relevant screens and implement an appropriate back-navigation flow.
17. Mini Project: Student Course Navigation App
Project objective: Create a React Native application that allows users to browse courses, view course details, and open a registration screen through stack navigation.
17.1 Required Screens
- Home: Displays a welcome message and a list of courses.
- Course Details: Displays the selected course name, ID, and description.
- Registration: Displays the selected course and a registration form.
- Confirmation: Shows a confirmation message after successful form validation.
17.2 Functional Requirements
- Register all screens in a native stack navigator.
- Open Course Details when the user selects a course.
- Pass course information to the details screen.
- Pass the selected course to the Registration screen.
- Validate required registration fields before proceeding.
- Display the Confirmation screen after successful validation.
- Use meaningful screen titles and test back navigation.
17.3 Expected Navigation Flow
Home
|
v
Course Details
|
v
Registration
|
v
Confirmation
Expected outcome: Users should be able to navigate through the course flow, transfer selected course information, and return to earlier screens when appropriate.
18. Interview Questions and Answers
Q1. What is stack navigation in React Native?
Answer: Stack navigation is a pattern in which screens are placed one above another. Users can open new screens and return to earlier screens through back navigation.
Q2. Which library is commonly used for stack navigation?
Answer: React Navigation is commonly used. Its @react-navigation/native-stack package provides native stack navigation.
Q3. What is the purpose of NavigationContainer?
Answer: It manages the navigation tree and navigation state. It normally wraps the root navigator of an application.
Q4. What does createNativeStackNavigator() do?
Answer: It creates a native stack navigator that can register screens and manage transitions between them.
Q5. What is the difference between Stack.Navigator and Stack.Screen?
Answer: Stack.Navigator defines the stack and shared settings. Stack.Screen registers an individual route and associates it with a screen component.
Q6. How do you navigate to another screen?
Answer: Use navigation.navigate('RouteName') with a route name registered in the relevant navigator.
Q7. How do you pass parameters to a screen?
Answer: Pass an object as the second argument, for example navigation.navigate('Profile', { userId: 10 }), and read it using route.params.
Q8. What is the difference between navigate() and push()?
Answer: navigate() navigates to a route according to the navigator's route-handling behavior. push() explicitly adds another route instance to the stack.
Q9. What does replace() do?
Answer: It replaces the current route with another route rather than keeping the current route as the immediate previous screen.
Q10. How can you change a screen's header title?
Answer: Set the screen's options prop, for example options={{ title: 'User Profile' }}.
Q11. What is initialRouteName?
Answer: It specifies the route displayed when the navigator's stack initializes.
Q12. Why might route.params be undefined?
Answer: Parameters may not have been supplied, or the screen may have been opened through another route. Read parameters safely and handle missing values.
Q13. What is the difference between stack and tab navigation?
Answer: Stack navigation manages a sequence of screens and their back history. Tab navigation allows users to switch between major application sections.
Q14. Does navigating to another screen always unmount the previous screen?
Answer: No. A previous screen can remain mounted in the stack. Focus-aware logic can be used when work must run whenever a screen becomes active.
Q15. What are common causes of navigation errors?
Answer: Common causes include missing dependencies, unregistered routes, incorrect route names, duplicate container configuration, and mismatched parameter names.
19. Quick Revision Checklist
- Stack navigation places screens in a navigation stack.
- The most recently added screen is usually displayed on top.
- Back navigation returns to an earlier screen when one exists.
NavigationContainer manages the navigation tree.
createNativeStackNavigator() creates a native stack navigator.
Stack.Navigator defines the stack configuration.
Stack.Screen registers individual screens.
navigation.navigate() navigates to a route.
navigation.goBack() requests back navigation.
navigation.push() pushes a new route instance.
navigation.replace() replaces the current route.
route.params reads passed parameters.
initialRouteName configures the initial route.
screenOptions configures shared screen settings.
- Route names must match the registered names.
- Navigation behavior should be tested on target platforms.
20. Conclusion
Stack navigation is an essential technique for developing multi-screen React Native applications. React Navigation's native stack navigator allows developers to register screens, choose the initial route, navigate forward and backward, pass parameters, and customize headers and transitions.
Begin with a simple Home and Profile flow, then practise larger navigation sequences such as product details, registration, checkout, and course navigation. Clear route names, safe parameter handling, thoughtful back-navigation behavior, and platform testing help create reliable mobile applications.
21. Additional Learning Resources