Setting up your React Native mobile application navigation with TypeScript is as simple as it can be. Understanding it only takes two steps.
If you understand the two steps involved, then you'd be able to structure any navigation for your react native app with full type checks.
- Type checking the navigator.
- Type checking the individual screens.
Let's create a native stack navigation structure with 3 screens: Home, Settings and Profile Screens.
1. Type Checking the Navigator
The first thing you need to do is to create an object type with mappings for route name to the params of the route. In our case, our Screens don't depend on any params, so we'd map the route name to undefined.
// I call this object type_ _RootStackParamList
export type RootStackParamList = {
Home: undefined;
Profile: undefined;
Settings: undefined;
};
Now when you create the navigator with createXNavigator (X can be Stack, BottomTab, Drawer), you have to let it know of the structure you created above.
//Instead of this
//const Stack = createNativeStackNavigator();
// Do this
const Stack = createNativeStackNavigator<RootStackParamList>();
Now we can create our Navigator as follows:
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={() => <View />} />
<Stack.Screen name="Profile" component={() => <View />} />
<Stack.Screen name="Settings" component={() => <View />} />
</Stack.Navigator>
The code above is fully type-checked, the name
props of the screens can only be one of the names defined in your RootStackParamList. **Enjoying the benefits of TypeScript already 😇.
This will provide type checking and IntelliSense for props of the Navigator
and Screen
components. The initialRouteName
propof the Navigator can only be one of the keys(Home, Profile, Settings) defined in the *RootStackParamList *object.
Now to the second step.
2. Type Checking Individual Screens
Why do we need to type check the individual screens, it's because we need to annotate the navigation
prop and the route
prop received by a screen. We want IntelliSense when we want to navigate to another screen, we don't want to keep going to the Navigator to check the name of our screens, we want TypeScript to show us the valid/available names.
// Every react-navigation screen receives route and navigation props
function ProfileScreen({ route, navigation }) {
// ...
}
The navigator packages in React Navigation export a generic type to define types for both the navigation
and route
props from the corresponding navigator.
For example, you can use NativeStackScreenProps
for the Native Stack Navigator (@react-navigation/native
), StackScreenProps
for Stack Navigator (@react-navigation/stack
), DrawerScreenProps
for Drawer Navigator (@react-navigation/drawer
), BottomTabScreenProps
for Bottom Tab Navigator (@react-navigation/bottom-tabs
) and so on.
For our case, we'd use NativeStackScreenProps
for the Native Stack Navigator.
This type takes 3 generics:
- The param list object is defined for the navigator (RootStackParamList)
- The name of the route to which the current screen belongs.
- The ID of the navigator (this is optional)
For example:
type Props = NativeStackScreenProps<RootStackParamList, "Profile", "MyStack">;
Create Params for the Navigator and Create Props for the Screen
Now let's apply this to our Profile Screen.
type ProfileProps = NativeStackScreenProps<RootStackParamList, "Profile">;
// Every react-navigation screen receives route and navigation props
function ProfileScreen({ route, navigation }: ProfileProps) {
// ...
}
This now allows us to type check route names and params which you're navigating using navigate
, push
etc. TypeScript now knows which screens are possible to navigate to, from the Profile Screen as seen below.
Intellisense on the navigate() function
For a scenario where our Settings Screen requires the userID
as a parameter, our param list would be like this:
export type RootStackParamList = {
Home: undefined;
Profile: undefined;
Settings: {
userId: number;
};
};
Now TypeScript highlights the navigation.navigate('Settings')
as seen below:
TypeScript complaining about the Settings route
This is because we defined the Settings route to accept the userID
parameter in the params list. This is exactly why we want our navigation to be type-checked by TypeScript.
What is needed?
Hovering over navigate shows what exactly is expected as seen above.
You can see the error has disappeared.
Now the error is gone after passing the params object withuserID
as the second parameter in the navigate function.
Enjoy structuring your React Navigation with proper type-check and intellisense provided by TypeScript.
As recommended by React Navigation, you can extract all the types to a separate file depending on your project.
types.ts
import {
createNativeStackNavigator,
NativeStackScreenProps,
} from "@react-navigation/native-stack";
import { Button, View } from "react-native";
export type RootStackParamList = {
Home: undefined;
Profile: undefined;
Settings: {
userId: number;
};
};
const Stack = createNativeStackNavigator<RootStackParamList>();
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={() => <View />} />
<Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen name="Settings" component={() => <View />} />
</Stack.Navigator>;
type ProfileProps = NativeStackScreenProps<RootStackParamList, "Profile">;
// Every react-navigation screen receives route and navigation props
function ProfileScreen({ route, navigation }: ProfileProps) {
return (
<View>
<Button
title="Go to Settings"
onPress={() => {
navigation.navigate("Settings", {
userId: 4,
});
}}
/>
</View>
);
}