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>
);
}