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.
