In this article of our series “React Native Essentials”, we will dive into how to navigation between screens in a React Native app using React Navigation.
React Navigation is a widely adopted navigation solution in the React Native ecosystem. If you’re new to the world of React Native, or even if you’ve been around for a while but haven’t had the chance to dive deep into its navigation capabilities, you’re in the right place!
Before React Navigation came into the picture, navigating between screens in a React Native application could feel like a Herculean task. Trust me, I’ve been there. I remember when I was just starting out with React Native, making an app transition from one screen to another felt like trying to solve a complex puzzle. But then, React Navigation came to the rescue and saved us all.
React Navigation is a JavaScript-based library that provides a way for your app to transition between screens, similar to how you would navigate between pages on a website. With a simple and consistent API, it makes the process of managing and implementing screen transitions in your React Native apps a breeze.
In the next sections, we’ll explore why you should consider using React Navigation, how to get it set up in your project, and how to start navigating between screens with ease. So, buckle up and get ready for a fun ride through the world of React Navigation!
Why Use React Navigation?
You might be wondering, “Why should I use React Navigation? Aren’t there other navigation libraries out there?” Well, you’re absolutely correct. There are indeed other options available for navigation in React Native, but let me share with you why React Navigation has become my go-to choice, and why it might be the right fit for your projects too.
Firstly, React Navigation is fully written in JavaScript, which aligns well with the rest of your React Native codebase. This means you can leverage your existing knowledge of JavaScript to understand and control your app’s navigation flow.
Secondly, React Navigation provides a highly customizable and flexible API. Whether you want to create a stack of screens, tabs, or a drawer sidebar — React Navigation has got you covered. Plus, it allows for seamless integration with other libraries in the React ecosystem.
Lastly, and my personal favorite, is the active and vibrant community behind React Navigation. The documentation is always up to date, and there are plenty of tutorials and guides available to help you get unstuck.
Setting Up React Navigation in Your Project
Before we begin, make sure you have a React Native project ready to go. If you’re unsure about this, check out the React Native documentation for a quick guide on setting up a new project.
Alright, let’s dive in:
Step 1: Install React Navigation
First, we need to install the @react-navigation/native
package. Open up your terminal, navigate to your project directory, and run the following command:
npm install @react-navigation/native
Step 2: Install Dependencies
React Navigation relies on some additional dependencies that we need to install. For a bare React Native project, you need to install the following libraries:
- If you have a Expo managed project :
npx expo install react-native-screens react-native-safe-area-context
- If you have a bare React Native project :
bash npm install react-native-screens react-native-safe-area-context
Step 3: Install Navigators
Now, we install the navigators that we will use in our project. For this guide, we will be using the stack navigator, so let’s install it:
npm install @react-navigation/stack
Step 4: Finalize Installation
If you’re using Expo, you are already set!
If you’re on a Mac and developing for iOS, make sure to install the pods:
cd ios && pod install && cd ..
Step 5: Set up the NavigationContainer
Next, import the necessary components at the top of your main app file (usually App.js
or index.js
):
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
After that, we need to wrap our app in the NavigationContainer
component. This component manages our app’s navigation state and links it to the React context:
export default function App() {
return (
<NavigationContainer>
{/* Your app code will go here */}
</NavigationContainer>
);
}
And voilà , React Navigation is now set up in your project!
Don’t worry if this feels a little overwhelming right now. As we delve deeper into the world of React Navigation, all these pieces will start to come together. In the next section, we’ll dive straight into navigating between screens, which is where the fun really begins!
Basics of Navigating Between Screens
Let’s start by understanding the core concepts of React Navigation:
- Screen: In React Navigation, a screen corresponds to a React Component. Each screen represents a standalone part of your app’s interface, and it’s where your users will interact with your app.
- Navigator: Navigators are what allow us to transition between screens. They maintain a history (or a ‘stack’) of screens and manage the transition animations when moving between these screens.
- Routes and parameters: When navigating to a screen, we can pass parameters to it. These parameters can be used to customize the screen or pass data, like you would on the Web with URL parameters.
Now, let’s put these concepts into action with a simple example:
NOTE : In these examples, we are using the Stack Navigator to host our screens. Don’t worry, we will explain in details what is a Stack Navigator in the next section.
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
// Define your screens
function HomeScreen({ navigation }) {
return (
<View style={styles.container}>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
function DetailsScreen() {
return (
<View style={styles.container}>
<Text>Details Screen</Text>
</View>
);
}
// Create a stack navigator
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
In this example, we’ve created two screens, HomeScreen
and DetailsScreen
. Each screen registered in the navigator receives a navigation
prop and a route
prop.
In HomeScreen
, we have a button that, when pressed, navigates to the DetailsScreen
using the navigation.navigate
function from the navigation
prop.
In the next section, we’ll explore the stack navigator in more detail, understand how it maintains a stack of screens, and discover how we can manipulate this stack to control our app’s navigation flow.
Deep Dive: The Stack Navigator
Now, it’s time to dive deeper and explore one of the most fundamental navigators – the Stack Navigator.
In my early days of coding, I used to imagine the Stack Navigator as a deck of cards. You place one card on top of the other (push a screen onto the stack), and you can also remove the top card to reveal the one underneath (pop a screen from the stack). This analogy helped me visualize the concept of a stack and understand how navigation works. Maybe it’ll help you too!
Creating a Stack Navigator
Creating a Stack Navigator is straightforward. Here’s a simple example:
import { createStackNavigator } from '@react-navigation/stack';
// Create a stack navigator
const Stack = createStackNavigator();
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
In this code snippet, HomeScreen
and DetailsScreen
are stacked on top of each other. The first screen in the stack (the bottommost card in the deck) is the initial screen that the user sees.
Navigating Between Screens
Navigating between screens in a stack navigator is done using the navigation.navigate
function. We’ve seen this in action in the previous section.
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
When the ‘Go to Details’ button is pressed, the app navigates to the DetailsScreen
.
Going Back
Navigating back to the previous screen is like removing the top card from the deck to reveal the one underneath. React Navigation provides the navigation.goBack
function for this purpose.
<Button
title="Go back"
onPress={() => navigation.goBack()}
/>
When the ‘Go back’ button is pressed, the app navigates back to the previous screen in the stack.
Exploring Other Navigators: Tab and Drawer Navigators
So far, we’ve seen how the Stack Navigator can help us manage and navigate between different screens in our app. But the beauty of React Navigation is that it offers even more navigators for different use cases. In this section, we’ll introduce two other popular navigators: Tab Navigator and Drawer Navigator.
Tab Navigator
Tab Navigator allows us to create a classic bottom tab navigation pattern, much like the one you see in many popular apps like Instagram or Twitter. This pattern is great when you want your users to switch between different main screens quickly.
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
);
}
In this example, we have two main screens, HomeScreen
and SettingsScreen
, and we can switch between them using the tabs at the bottom of the screen.
Drawer Navigator
Drawer Navigator, on the other hand, provides a sidebar menu for your app. This is a common pattern in many apps when you have multiple screens and options, and you want to keep your interface uncluttered.
Here’s a simple example of setting up a Drawer Navigator:
import { createDrawerNavigator } from '@react-navigation/drawer';
const Drawer = createDrawerNavigator();
function MyDrawer() {
return (
<Drawer.Navigator>
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Notifications" component={NotificationsScreen} />
</Drawer.Navigator>
);
}
In this example, the HomeScreen
and NotificationsScreen
are accessible via a drawer menu that can be opened by swiping from the left edge of the screen or by tapping the hamburger icon in the header (if provided).
Passing Parameters Between Screens
As we navigate through the many screens of our React Native app, we often need to pass data from one screen to another. For instance, you might have a product list screen, and when a user taps on a product, you’d want to pass the product’s details to the product detail screen. This is where passing parameters between screens comes in.
React Navigation makes passing parameters between screens a breeze. In this section, I’ll show you how to pass parameters from one screen to another and how to read these parameters in the destination screen.
Passing Parameters to a Route
When calling navigation.navigate
, you can pass parameters to the route you are navigating to. These parameters are then available in the destination route through the route.params
property. Here’s how you can pass parameters to a route:
function ProductsListScreen({ navigation }) {
return (
<Button
title="Go to ProductDetails"
onPress={() => {
/* 1. Navigate to the ProductDetails route with params */
navigation.navigate('ProductDetails', {
itemId: 86,
otherParam: 'anything you want here',
});
}}
/>
);
}
In this example, we’re passing two parameters (itemId
and otherParam
) to the ‘ProductDetails’ route.
Reading Parameters in a Route
As we have seen in the section 3, each screen registered in the navigator receives a navigation
prop and a route
prop.
Once you’ve passed parameters to a route, you can access these in the destination route through the route.params
property. Here’s how you can read parameters in a route:
function ProductDetailsScreen({ route }) {
/* 2. Get the params */
const { itemId, otherParam } = route.params;
return (
<Text>ItemId: {itemId}</Text>
<Text>Other Param: {otherParam}</Text>
);
}
In this example, we’re reading the itemId
and otherParam
parameters in the ‘ProductDetails’ route, and we display them on screen.
Customizing Screen Headers
As we navigate through the different screens of our app, it’s often helpful to provide users with a visual indication of their current location within the app. This is where screen headers come in. Not only do they serve as a navigational aid, but they can also enhance the overall look and feel of your app.
By default, the Stack Navigator provides a header, but its style may not align with your app’s aesthetic. Good news! React Navigation allows us to customize screen headers to match our app’s design language. In this section, we’ll learn how to do just that.
Customizing the Header Title
The simplest way to customize the header is by changing the title. You can do this by setting the title
option in the options
prop of the Screen
component.
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'My Home' }}
/>
In this example, the header title for the ‘Home’ screen is set to ‘My Home’.
Customizing the Header Style
You can also customize the style of the header. The headerStyle
option lets you change the background color of the header, while the headerTintColor
option changes the color of the header text and back button.
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
title: 'My Home',
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
}}
/>
Here, the header for the ‘Home’ screen has a background color of ‘#f4511e’ and a text color of ‘#fff’.
Hiding the Header
There might be some scenarios where you want to hide the header. To do this, you can set the headerShown
option to false
.
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerShown: false,
}}
/>
Advanced Techniques in React Navigation
These techniques can help you build more complex and feature-rich navigation flows to enhance your app’s user experience. In this section, we’ll discuss nested navigators, conditional rendering of routes, and dynamic navigation.
Nested Navigators
React Navigation allows you to nest navigators within other navigators. This is useful when you need different types of navigation patterns in different parts of your app. For instance, you might have a tab navigator within a stack navigator. This is possible because a Navigator is also a React component!
Here’s an example of how you can set up nested navigators:
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
function HomeTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Feed" component={FeedScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
}
function App() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeTabs} /> // <-- The Tab Navigator
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
);
}
Here, we have a tab navigator (HomeTabs
) nested within a stack navigator. The HomeTabs
navigator contains two screens (FeedScreen
and ProfileScreen
), and the stack navigator contains the HomeTabs
and DetailsScreen
.
Conditional Rendering of Routes
Sometimes, you might need to render different routes based on certain conditions. For instance, you might want to show a login screen to unauthenticated users and a home screen to authenticated users. You can achieve this by conditionally rendering routes.
Here’s an example:
function App() {
const { isAuthenticated } = useAuth();
return (
<Stack.Navigator>
{isAuthenticated ? (
<Stack.Screen name="Home" component={HomeScreen} />
) : (
<Stack.Screen name="Login" component={LoginScreen} />
)}
</Stack.Navigator>
);
}
In this example, we’re using a hypothetical useAuth
hook that returns a boolean indicating whether the user is authenticated or not. Depending on the value of isAuthenticated
, we render the HomeScreen
or LoginScreen
.
Dynamic Navigation
In some cases, you might not know the screens a user can navigate to at compile time. In such cases, you can use dynamic navigation. This allows you to define screens in a navigator dynamically.
Here’s an example:
function App() {
const screens = useScreensList(); // Fetch screens from an API
return (
<Stack.Navigator>
{screens.map((screen) => (
<Stack.Screen
key={screen.id}
name={screen.name}
component={screen.component}
/>
))}
</Stack.Navigator>
);
}
In this example, we’re fetching a list of screens from a hypothetical API using the useScreensList
hook. We then map over the screens
array to create a Screen
for each item.
Wrapping up
That’s it! We’ve come a long way! From setting up React Navigation in our projects to navigating between screens, passing parameters, and even customizing headers, we’ve covered a lot of ground about getting started with React Navigation.
I encourage you to take what you’ve learned in this article and apply it to your own projects. Start small, perhaps by adding a new screen to your app, and then gradually experiment with more advanced features like nested navigators and dynamic navigation.
I hope this guide has given you the confidence to start building your own navigation flows in your apps.
If you ever find yourself stuck, don’t forget to check the React Navigation documentation. It’s an excellent resource that’s very well written and always there to help you.
Looking forward to seeing you in the next post! For more React Native resources, tips, and tutorials, be sure to stay tuned to React Native Central.
To further your understanding of React Native, have a look at some of our other articles: