React Native Stack and Tab Navigation

Stack navigation and tab navigation are the two most common patterns in mobile apps. Stack navigation takes users deeper into content. Tab navigation switches between top-level sections. Most apps combine both.

Stack Navigator

Stack navigation works like a stack of cards. Each new screen gets placed on top. Going back removes the top card. The header shows a back arrow automatically.

Initial State:          After navigate('Details'):   After goBack():
┌─────────────┐         ┌─────────────┐              ┌─────────────┐
│    Home     │         │   Details   │◄─ top card   │    Home     │
└─────────────┘         ├─────────────┤              └─────────────┘
                        │    Home     │
                        └─────────────┘

Setting Up a Stack Navigator

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

const Stack = createNativeStackNavigator();

function HomeScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text style={{ fontSize: 24 }}>Home Screen</Text>
      <TouchableOpacity onPress={() => navigation.navigate('Details', { id: 1 })}>
        <Text>Open Details</Text>
      </TouchableOpacity>
    </View>
  );
}

function DetailsScreen({ route, navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Details for item {route.params.id}</Text>
      <TouchableOpacity onPress={() => navigation.goBack()}>
        <Text>← Back</Text>
      </TouchableOpacity>
    </View>
  );
}

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

Customising the Stack Header

<Stack.Navigator
  screenOptions={{
    headerStyle: { backgroundColor: '#007AFF' },
    headerTintColor: '#fff',           // back arrow and title color
    headerTitleStyle: { fontWeight: 'bold' },
  }}
>
  <Stack.Screen
    name="Home"
    component={HomeScreen}
    options={{ title: 'Welcome' }}     // custom title per screen
  />
  <Stack.Screen
    name="Details"
    component={DetailsScreen}
    options={({ route }) => ({ title: `Item ${route.params.id}` })}
  />
</Stack.Navigator>

Tab Navigator

Tab navigation shows a row of tabs at the bottom of the screen. Each tab switches to a different top-level section. Instagram, Twitter, and most social apps use bottom tabs.

┌─────────────────────────────────────┐
│                                     │
│          Screen Content             │
│                                     │
│                                     │
├─────────────────────────────────────┤
│  🏠 Home  │ 🔍 Search  │  👤 Profile│  ← Tab Bar
└─────────────────────────────────────┘

Setting Up Bottom Tabs

import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

const Tab = createBottomTabNavigator();

function HomeScreen() {
  return <View><Text>Home Screen</Text></View>;
}

function SearchScreen() {
  return <View><Text>Search Screen</Text></View>;
}

function ProfileScreen() {
  return <View><Text>Profile Screen</Text></View>;
}

export default function App() {
  return (
    <NavigationContainer>
      <Tab.Navigator>
        <Tab.Screen name="Home" component={HomeScreen} />
        <Tab.Screen name="Search" component={SearchScreen} />
        <Tab.Screen name="Profile" component={ProfileScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

Adding Icons to Tabs

Install the icon library first:

npx expo install @expo/vector-icons
import { Ionicons } from '@expo/vector-icons';

<Tab.Navigator
  screenOptions={({ route }) => ({
    tabBarIcon: ({ focused, color, size }) => {
      let iconName;
      if (route.name === 'Home') {
        iconName = focused ? 'home' : 'home-outline';
      } else if (route.name === 'Search') {
        iconName = focused ? 'search' : 'search-outline';
      } else if (route.name === 'Profile') {
        iconName = focused ? 'person' : 'person-outline';
      }
      return <Ionicons name={iconName} size={size} color={color} />;
    },
    tabBarActiveTintColor: '#007AFF',
    tabBarInactiveTintColor: '#999',
  })}
>
Tab Bar Result:
┌────────────────────────────────────────────┐
│  🏠         🔍          👤                 │
│ Home      Search      Profile              │
│ (blue)    (grey)      (grey)               │
└────────────────────────────────────────────┘

Combining Stack Inside Tabs

Real apps nest stacks inside tabs. The Home tab has its own stack — Home List → Product Detail → Checkout. The Profile tab has its own stack — Profile → Edit Profile. Each tab keeps its own navigation history independently.

App Structure:
┌────────────────────────────────────────────────────┐
│  Tab Navigator                                     │
│  ├── Home Tab (Stack Navigator)                    │
│  │   ├── Screen: HomeList                          │
│  │   ├── Screen: ProductDetail                     │
│  │   └── Screen: Checkout                          │
│  ├── Search Tab (Stack Navigator)                  │
│  │   ├── Screen: SearchMain                        │
│  │   └── Screen: SearchResult                      │
│  └── Profile Tab (Stack Navigator)                 │
│      ├── Screen: ProfileMain                       │
│      └── Screen: EditProfile                       │
└────────────────────────────────────────────────────┘
const HomeStack = createNativeStackNavigator();

function HomeStackNavigator() {
  return (
    <HomeStack.Navigator>
      <HomeStack.Screen name="HomeList" component={HomeListScreen} />
      <HomeStack.Screen name="ProductDetail" component={ProductDetailScreen} />
    </HomeStack.Navigator>
  );
}

// Then use HomeStackNavigator as the component for the Home Tab
<Tab.Screen name="Home" component={HomeStackNavigator} />

Adding a Badge to a Tab

Show a number badge on a tab icon — useful for notifications or cart counts.

<Tab.Screen
  name="Cart"
  component={CartScreen}
  options={{ tabBarBadge: 3 }}   // shows a red "3" badge
/>

┌──────────────────────────────────┐
│  🏠       🔍      🛒³    👤      │
│ Home    Search   Cart  Profile   │
│                   ↑              │
│              red badge "3"       │
└──────────────────────────────────┘

Summary

Stack navigation pushes screens on top of each other and uses a back arrow to go back. Tab navigation switches between independent sections at the root of the app. Nesting a stack inside each tab is the standard pattern for production apps. Tab icons and badges make the navigation bar informative and clear for users.

Leave a Comment