React Navigation v6 with TypeScript

ā€¢

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.

  1. Type checking the navigator.
  2. 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Ā initialRouteNamepropof 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:

  1. The param list object is defined for the navigator (RootStackParamList)
  2. The name of the route to which the current screen belongs.
  3. The ID of the navigator (this is optional)

For example:

type Props = NativeStackScreenProps<RootStackParamList, "Profile", "MyStack">;

image

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.

image

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:

image

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.

image

What is needed?

Hovering over navigate shows what exactly is expected as seen above.

image

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

Enjoyed this article?

Share it with your network to help others discover it

Continue Learning

Discover more articles on similar topics