Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly type useNavigation in React Navigation?

I'm trying to type the useNavigation from React Navigation. I would like to be able to pass only the name of the route, but I get an error unless I also pass props for that route.

Following the documentation, I understand the implementation should look something like this:

import { StackNavigationProp } from '@react-navigation/stack';

type StackParamList = {
    Home: { foo: string, onBar: () => void }
    About: AboutProps
}

type NavigationProps = StackNavigationProp<StackParamList>

const MyComponent = () => {
  const navigation = useNavigation<NavigationProps>()

  const handleOnNavigate = () => navigation.navigate('Home')
  //                                                    ^ TypeError!

I am geting a TypeError on the last line there. If I add the props for that route, the error disappears, but I shouldn't have to do that.

navigation.navigate('Home', { foo: 'hello', onBar: () => {} })

Looking at the type declaration for navigation.navigate (see below), this should not be necessary, as there is an overload for simply passing the name of the route as the only argument. I must be doing something wrong, since that is not accepted ... but what, where and why?

Here is a CodeSandBox reproducing the TypeError.

React Navigation types.d.ts (link)

navigate<RouteName extends keyof ParamList>(...args: undefined extends ParamList[RouteName]
  ? [screen: RouteName] | [screen: RouteName, params: ParamList[RouteName]]
  : [screen: RouteName, params: ParamList[RouteName]])
: void;
like image 612
Nix Avatar asked Sep 02 '25 17:09

Nix


1 Answers

When using Typescript the whole navigation should be strictly typed. Thankfully we don't need to write wrappers for their implementations, because we are supplied generic types.

The simplest way I found to add types to the useNavigation hook is the following:

The RootStackParamList type implementation needs to be altered if your route takes arguments

// src/_app.tsx
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";

import HomeScreen from "./screens/home";
import AuthScreen from "./screens/auth";

export type ScreenNames = ["Home", "Auth"] // type these manually
export type RootStackParamList = Record<ScreenNames[number], undefined>;
export type StackNavigation = NavigationProp<RootStackParamList>;

const Stack = createNativeStackNavigator<RootStackParamList>();

export const App = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Auth" component={AuthScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
};
// src/screens/home.tsx
import { useNavigation } from '@react-navigation/native';
import { type StackNavigation } from "../_app";

const HomeScreen = () => {
  const { navigate } = useNavigation<StackNavigation>();

  const handleOnNavigate = () => navigate("Home");

  // ... the rest of the component code
}

export default HomeScreen;

Also note that they don't use the useNavigation hook for pages/screens, rather deeply nested components, since the navigator already gets passed as a prop to the components passed into the Stack.Screen, meaning you can also use the HomeScreen's navigator the following way:

// src/screens/home.tsx
import { type StackNavigation } from "../_app";

interface HomeScreenProps {
  navigation: StackNavigation;
}

const HomeScreen: React.FC<HomeScreenProps> = ({ navigation }) => {
  const handleOnNavigate = () => navigation.navigate("Home");

  // ... the rest of the component code
}

export default HomeScreen;
like image 169
Dirk Beukes Avatar answered Sep 05 '25 07:09

Dirk Beukes