modern-app-patterns

Navigation with React Navigation

Type-safe navigation with nested stacks and tabs.

Pattern

Example

// navigation/types.ts
export type RootStackParamList = {
  Home: undefined;
  Details: { id: string };
};
// AppNavigator.tsx
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import type { RootStackParamList } from "./navigation/types";

const Stack = createNativeStackNavigator<RootStackParamList>();

export function AppNavigator() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Details" component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

Why it works


Live end-to-end example (copy/paste)

Typed root stack + two screens navigating with params.

// navigation/types.ts
export type RootStackParamList = { Home: undefined; Details: { id: string } };
// screens/HomeScreen.tsx
import React from "react";
import { View, Text, Button } from "react-native";
import type { NativeStackScreenProps } from "@react-navigation/native-stack";
import type { RootStackParamList } from "../navigation/types";

type Props = NativeStackScreenProps<RootStackParamList, "Home">;
export function HomeScreen({ navigation }: Props) {
  return (
    <View style={{ padding: 16 }}>
      <Text>Home</Text>
      <Button
        title="Go to Details 42"
        onPress={() => navigation.navigate("Details", { id: "42" })}
      />
    </View>
  );
}
// screens/DetailsScreen.tsx
import React from "react";
import { View, Text } from "react-native";
import type { NativeStackScreenProps } from "@react-navigation/native-stack";
import type { RootStackParamList } from "../navigation/types";

type Props = NativeStackScreenProps<RootStackParamList, "Details">;
export function DetailsScreen({ route }: Props) {
  return (
    <View style={{ padding: 16 }}>
      <Text>Details for {route.params.id}</Text>
    </View>
  );
}
// AppNavigator.tsx
import React from "react";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import type { RootStackParamList } from "./navigation/types";
import { HomeScreen } from "./screens/HomeScreen";
import { DetailsScreen } from "./screens/DetailsScreen";

const Stack = createNativeStackNavigator<RootStackParamList>();

export function AppNavigator() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Details" component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

Notes

Sandbox copy map

Paste into an Expo app (see sandboxes/react-native-expo):