Navigations in React Native : Stack, Tabs, Params & Type Safety (For Beginners)

Aman GuptaAman Gupta
3 min read

A clear, beginner-friendly guide using analogies, real-world examples, and working code to understand how to navigate React Native apps with Stack and Tab navigators using TypeScript.

What We’ll Learn

  1. Why we use Stack and Tab navigators together

  2. How navigation works like Google Maps

  3. What undefined in route types means

  4. Why and how to pass data between screens

  5. What CompositeScreenProps really does

  6. Full working code: App.tsx + TabNavigator + HomeScreen + Route Types


1. Why Use Stack + Tab Navigators?

Imagine your app is like a hotel:

  • Tabs = Floors (Home, Orders, Cart)

  • Stack Screens = Rooms on each floor (Details, Payment)

Users walk around using Tabs to switch floors, and use Stack navigation to enter specific rooms.

That’s why we use both together:

  • Tabs for main sections.

  • Stack for drill-down or detail screens.


2. How React Navigation Works (Google Maps Analogy)

Think of React Navigation like a Google Maps GPS system:

  • You enter a location like Details.

  • It expects a specific format, like { id: string }.

  • If you don't give the correct route data, it's like giving an incomplete address.

3. What Does undefined Mean in Route Types?

In this type:

export type RootTabParamList = {
  Home: undefined;
  Cart: undefined;
}

undefined means: “This route doesn’t need any parameters.”

But for routes with parameters, like Details:

export type RootStackParamList = {
  Details: { id: string };
}

This says: “To go to Details, you must pass an { id: string }.”


4. How to Pass Data Between Screens

Going from Home → Details:

navigation.push('Details', { id: 'abc123' });

In DetailsScreen.tsx, read it like:

const route = useRoute<NativeStackScreenProps<RootStackParamList, 'Details'>['route']>();
const { id } = route.params;

5. What Is CompositeScreenProps and Why We Need It

You're inside a tab (like Home) but also need access to the full-stack route (like pushing to Details). That’s when CompositeScreenProps is like a “combo-key” that gives you access to both doors.

type Props = CompositeScreenProps<
  BottomTabScreenProps<RootTabParamList, 'Home'>,
  NativeStackScreenProps<RootStackParamList>
>;
  • 👆 It merges types from Tab and Stack, so you can use navigation.push('Details') from inside a tab screen.

Full Code

src/types/navigation.ts

export type RootStackParamList = {
  Tab: undefined;
  Details: { id: string };
  Payment: undefined;
};

export type RootTabParamList = {
  Home: undefined;
  Cart: undefined;
  Favorite: undefined;
  Orders: undefined;
};

App.tsx

import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import TabNavigator from './src/navigator/TabNavigator';
import DetailsScreen from './src/screens/DetailsScreen';
import PaymentScreen from './src/screens/PaymentScreen';
import { RootStackParamList } from './src/types/navigation';

const Stack = createNativeStackNavigator<RootStackParamList>();

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator screenOptions={{ headerShown: false }}>
        <Stack.Screen name="Tab" component={TabNavigator} />
        <Stack.Screen name="Details" component={DetailsScreen} />
        <Stack.Screen name="Payment" component={PaymentScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

TabNavigator.tsx

import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import HomeScreen from '../screens/HomeScreen';
import CartScreen from '../screens/CartScreen';
import FavoritesScreen from '../screens/FavoritesScreen';
import OrderScreen from '../screens/OrderScreen';
import { RootTabParamList } from '../types/navigation';

const Tab = createBottomTabNavigator<RootTabParamList>();

export default function TabNavigator() {
  return (
    <Tab.Navigator screenOptions={{ headerShown: false }}>
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Cart" component={CartScreen} />
      <Tab.Screen name="Favorite" component={FavoritesScreen} />
      <Tab.Screen name="Orders" component={OrderScreen} />
    </Tab.Navigator>
  );
}

HomeScreen.tsx

import { View, Text, TouchableOpacity } from 'react-native';
import { CompositeScreenProps } from '@react-navigation/native';
import { BottomTabScreenProps } from '@react-navigation/bottom-tabs';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { RootTabParamList, RootStackParamList } from '../types/navigation';

type Props = CompositeScreenProps<
  BottomTabScreenProps<RootTabParamList, 'Home'>,
  NativeStackScreenProps<RootStackParamList>
>;

export default function HomeScreen({ navigation }: Props) {
  return (
    <View>
      <Text>Home</Text>
      <TouchableOpacity onPress={() => navigation.push('Details', { id: 'abc123' })}>
        <Text>Go to Details</Text>
      </TouchableOpacity>
    </View>
  );
}

Final Thoughts

You just learned how to build real-world navigation in React Native with:

Stack & Tab navigation
Type-safe route params
Accessing stack routes from tab screens
The mysterious undefined
CompositeScreenProps made easy

If this helped you, share it with another dev .

1
Subscribe to my newsletter

Read articles from Aman Gupta directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Aman Gupta
Aman Gupta

I am a passionate Frontend Developer who thrives on turning complex ideas into compelling, scalable, and user-centric digital experiences. Over time, I’ve successfully developed a diverse range of projects, including: My technical expertise spans JavaScript, TypeScript, React, Redux, Node.js, Express.js, Next.js, HTML, CSS, and Git, allowing me to craft efficient, responsive, and visually appealing applications. I am driven by continuous learning and adapt as technology evolves. Working within agile teams has honed my communication and collaboration skills, ensuring that I can translate requirements into robust solutions. Whether it’s building a production-ready web applications with robust AI integrations, delivering intelligent solutions that adapt to evolving market needs and end-user expectations, integrating secure payments, or optimizing code architecture and performance, I approach each project with a commitment to innovation, scalability, and user satisfaction. Let’s work together to create digital solutions that solve real-world challenges, delight end-users, and drive meaningful results!