🧭

React Native Navigation

Master navigation patterns in React Native apps

Navigation is crucial for mobile apps. React Navigation is the most popular navigation library for React Native, providing stack, tab, and drawer navigation patterns with smooth animations and gesture support.

Getting Started with React Navigation

Installation

# Install core navigation library
npm install @react-navigation/native

# Install required dependencies
npm install react-native-screens react-native-safe-area-context

# For iOS, run pod install
cd ios && pod install && cd ..

# For Expo projects, install Expo-compatible versions
npx expo install react-native-screens react-native-safe-area-context

Basic Setup

// App.js
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';

export default function App() {
  return (
    <NavigationContainer>
      {/* Your navigators go here */}
    </NavigationContainer>
  );
}

Navigation Types

Stack Navigation

Navigate between screens in a stack-like manner

Use Case: Perfect for hierarchical navigation like Settings → Profile → Edit Profile

npm install @react-navigation/native @react-navigation/native-stack

Example Implementation:

import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen 
          name="Home" 
          component={HomeScreen}
          options={{ title: 'Welcome' }}
        />
        <Stack.Screen 
          name="Details" 
          component={DetailsScreen}
          options={{ title: 'Details' }}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

Tab Navigation

Bottom tab navigation for main app sections

Use Case: Main app navigation like Home, Search, Profile, Settings tabs

npm install @react-navigation/bottom-tabs

Example Implementation:

import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Ionicons } from '@expo/vector-icons';

const Tab = createBottomTabNavigator();

function MyTabs() {
  return (
    <Tab.Navigator
      screenOptions={({ route }) => ({
        tabBarIcon: ({ focused, color, size }) => {
          let iconName;
          if (route.name === 'Home') {
            iconName = focused ? 'home' : 'home-outline';
          } else if (route.name === 'Settings') {
            iconName = focused ? 'settings' : 'settings-outline';
          }
          return <Ionicons name={iconName} size={size} color={color} />;
        },
        tabBarActiveTintColor: 'tomato',
        tabBarInactiveTintColor: 'gray',
      })}
    >
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Settings" component={SettingsScreen} />
    </Tab.Navigator>
  );
}

Drawer Navigation

Side drawer navigation for additional options

Use Case: Secondary navigation, user menu, or app settings access

npm install @react-navigation/drawer react-native-gesture-handler react-native-reanimated

Example Implementation:

import { createDrawerNavigator } from '@react-navigation/drawer';

const Drawer = createDrawerNavigator();

function MyDrawer() {
  return (
    <Drawer.Navigator
      screenOptions={{
        drawerStyle: {
          backgroundColor: '#c6cbef',
          width: 240,
        },
        drawerActiveTintColor: '#e91e63',
        drawerLabelStyle: {
          marginLeft: -25,
          fontFamily: 'Roboto-Medium',
          fontSize: 15,
        },
      }}
    >
      <Drawer.Screen name="Home" component={HomeScreen} />
      <Drawer.Screen name="Profile" component={ProfileScreen} />
      <Drawer.Screen name="Settings" component={SettingsScreen} />
    </Drawer.Navigator>
  );
}

Navigation Hooks

useNavigation

Access navigation object from any component

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

function MyComponent() {
  const navigation = useNavigation();

  return (
    <TouchableOpacity
      onPress={() => navigation.navigate('Details', { itemId: 86 })}
    >
      <Text>Go to Details</Text>
    </TouchableOpacity>
  );
}

useRoute

Access route object and parameters

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

function DetailsScreen() {
  const route = useRoute();
  const { itemId, otherParam } = route.params;

  return (
    <View>
      <Text>Details Screen</Text>
      <Text>Item ID: {itemId}</Text>
      <Text>Other Param: {otherParam}</Text>
    </View>
  );
}

useFocusEffect

Run side effects when screen comes into focus

import { useFocusEffect } from '@react-navigation/native';
import { useCallback } from 'react';

function ProfileScreen() {
  useFocusEffect(
    useCallback(() => {
      // Do something when the screen is focused
      console.log('Screen is focused');
      
      return () => {
        // Do something when the screen is unfocused
        console.log('Screen is unfocused');
      };
    }, [])
  );

  return <View>{/* Screen content */}</View>;
}

Complete Navigation Example

App with Stack + Tab Navigation

import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Ionicons } from '@expo/vector-icons';

const Stack = createNativeStackNavigator();
const Tab = createBottomTabNavigator();

// Screen Components
function HomeScreen({ navigation }) {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Home Screen</Text>
      <TouchableOpacity
        style={styles.button}
        onPress={() => navigation.navigate('Details', { 
          itemId: 86, 
          otherParam: 'anything you want here' 
        })}
      >
        <Text style={styles.buttonText}>Go to Details</Text>
      </TouchableOpacity>
    </View>
  );
}

function DetailsScreen({ route, navigation }) {
  const { itemId, otherParam } = route.params;

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Details Screen</Text>
      <Text>Item ID: {JSON.stringify(itemId)}</Text>
      <Text>Other Param: {JSON.stringify(otherParam)}</Text>
      <TouchableOpacity
        style={styles.button}
        onPress={() => navigation.goBack()}
      >
        <Text style={styles.buttonText}>Go Back</Text>
      </TouchableOpacity>
    </View>
  );
}

function ProfileScreen() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Profile Screen</Text>
    </View>
  );
}

function SettingsScreen() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Settings Screen</Text>
    </View>
  );
}

// Stack Navigator for Home flow
function HomeStack() {
  return (
    <Stack.Navigator>
      <Stack.Screen 
        name="HomeMain" 
        component={HomeScreen}
        options={{ title: 'Home' }}
      />
      <Stack.Screen 
        name="Details" 
        component={DetailsScreen}
        options={{ title: 'Details' }}
      />
    </Stack.Navigator>
  );
}

// Tab Navigator
function TabNavigator() {
  return (
    <Tab.Navigator
      screenOptions={({ route }) => ({
        tabBarIcon: ({ focused, color, size }) => {
          let iconName;

          if (route.name === 'Home') {
            iconName = focused ? 'home' : 'home-outline';
          } else if (route.name === 'Profile') {
            iconName = focused ? 'person' : 'person-outline';
          } else if (route.name === 'Settings') {
            iconName = focused ? 'settings' : 'settings-outline';
          }

          return <Ionicons name={iconName} size={size} color={color} />;
        },
        tabBarActiveTintColor: '#007AFF',
        tabBarInactiveTintColor: 'gray',
        headerShown: false,
      })}
    >
      <Tab.Screen name="Home" component={HomeStack} />
      <Tab.Screen name="Profile" component={ProfileScreen} />
      <Tab.Screen name="Settings" component={SettingsScreen} />
    </Tab.Navigator>
  );
}

// Main App Component
export default function App() {
  return (
    <NavigationContainer>
      <TabNavigator />
    </NavigationContainer>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    paddingHorizontal: 20,
    paddingVertical: 10,
    borderRadius: 8,
    marginTop: 10,
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
    fontWeight: 'bold',
  },
});

Advanced Navigation Patterns

Deep Linking

Handle URLs that open specific screens in your app

const linking = {
  prefixes: ['myapp://'],
  config: {
    screens: {
      Home: 'home',
      Profile: 'profile/:userId',
    },
  },
};

<NavigationContainer linking={linking}>
  {/* navigators */}
</NavigationContainer>

Authentication Flow

Conditional navigation based on authentication state

function App() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  return (
    <NavigationContainer>
      {isLoggedIn ? (
        <MainTabNavigator />
      ) : (
        <AuthStackNavigator />
      )}
    </NavigationContainer>
  );
}

Modal Screens

Present screens as modals with custom animations

<Stack.Navigator>
  <Stack.Group>
    <Stack.Screen name="Home" component={Home} />
  </Stack.Group>
  <Stack.Group screenOptions={{ presentation: 'modal' }}>
    <Stack.Screen name="MyModal" component={Modal} />
  </Stack.Group>
</Stack.Navigator>

Custom Transitions

Create custom screen transition animations

<Stack.Screen
  name="Profile"
  component={Profile}
  options={{
    transitionSpec: {
      open: { animation: 'timing', config: { duration: 500 } },
      close: { animation: 'timing', config: { duration: 500 } },
    },
  }}
/>

Navigation Best Practices

✅ Best Practices

  • • Use TypeScript for type-safe navigation
  • • Implement proper deep linking
  • • Handle navigation state persistence
  • • Use appropriate navigation patterns for your app
  • • Test navigation flows thoroughly
  • • Implement proper loading and error states

❌ Common Mistakes

  • • Don't nest navigators unnecessarily
  • • Avoid complex navigation hierarchies
  • • Don't ignore platform-specific navigation patterns
  • • Avoid passing large objects as route params
  • • Don't forget to handle back button on Android
  • • Avoid blocking navigation with heavy operations