Skip to main content
Version: 7.x

Type checking with TypeScript

React Navigation can be configured to type-check screens and their params, as well as various other APIs using TypeScript. This provides better intelliSense and type safety when working with React Navigation.

note

React Navigation is designed to work with strict mode in TypeScript. If you are not using strict mode, some things might not work as expected.

There are 2 steps to configure TypeScript with the static API:

  1. Each screen component needs to specify the type of the route.params prop that it accepts. The StaticScreenProps type makes it simpler:

    import type { StaticScreenProps } from '@react-navigation/native';

    type Props = StaticScreenProps<{
    username: string;
    }>;

    function ProfileScreen({ route }: Props) {
    // ...
    }
  2. Generate the ParamList type for the root navigator and specify it as the default type for the RootParamList type:

    import type { StaticParamList } from '@react-navigation/native';

    const HomeTabs = createBottomTabNavigator({
    screens: {
    Feed: FeedScreen,
    Profile: ProfileScreen,
    },
    });

    const RootStack = createNativeStackNavigator({
    screens: {
    Home: HomeTabs,
    },
    });

    type RootStackParamList = StaticParamList<typeof RootStack>;

    declare global {
    namespace ReactNavigation {
    interface RootParamList extends RootStackParamList {}
    }
    }

    This is needed to type-check the useNavigation hook.

Generally, we recommend using the default types for the useNavigation prop to access the navigation object in a navigator-agnostic manner. However, if you need to use navigator-specific APIs, you need to manually annotate useNavigation:

type BottomTabParamList = StaticParamList<typeof BottomTabNavigator>;
type ProfileScreenNavigationProp = BottomTabNavigationProp<
BottomTabParamList,
'Profile'
>;

// ...

const navigation = useNavigation<ProfileScreenNavigationProp>();

Note that annotating useNavigation this way is not type-safe since we can't guarantee that the type you provided matches the type of the navigator.

Nesting navigator using dynamic API

Consider the following example:

const Tab = createBottomTabNavigator();

function HomeTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Feed" component={FeedScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
}

const RootStack = createStackNavigator({
Home: HomeTabs,
});

Here, the HomeTabs component is defined using the dynamic API. This means that when we create the param list for the root navigator with StaticParamList<typeof RootStack>, it won't know about the screens defined in the nested navigator. To fix this, we'd need to specify the param list for the nested navigator explicitly.

This can be done by using the type of the route prop that the screen component receives:

type HomeTabsParamList = {
Feed: undefined;
Profile: undefined;
};

type HomeTabsProps = StaticScreenProps<
NavigatorScreenParams<HomeTabsParamList>
>;

function HomeTabs(_: HomeTabsProps) {
return (
<Tab.Navigator>
<Tab.Screen name="Feed" component={FeedScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
}

Now, when using StaticParamList<typeof RootStack>, it will include the screens defined in the nested navigator.