Combining Bottom Tabs with Independent Stacks in React Navigation v6
Learn how to nest stack navigators inside bottom tabs in React Navigation v6 to give each tab its own navigation history while keeping a shared tab bar.
02 Jun 2026, 10:09 UTC

Problem: Sharing a tab bar while keeping separate navigation histories
Many mobile apps need a persistent bottom tab bar (e.g., Home, Search, Profile) but also want each tab to maintain its own navigation stack so users can drill into details without losing their place when they switch tabs. Configuring this correctly avoids duplicated state and keeps the tab bar visible across screens.
Thesis
By nesting a createStackNavigator inside each tab of a createBottomTabNavigator in React Navigation v6, you get independent stacks per tab, lazy‑mounted screens, and straightforward deep‑link handling—while still sharing a single tab bar.
Setting up the navigators
First install the required packages (Expo or bare React Native):
npm install @react-navigation/native @react-navigation/native-stack @react-navigation/bottom-tabs
# plus peer dependencies: react-native-screens react-native-safe-area-context
Then define a stack for each tab and place those stacks as screens in the bottom tab navigator.
Example code
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
// --- Stack for Home tab ---
const HomeStack = createNativeStackNavigator();
function HomeStackScreen() {
return (
);
}
// --- Stack for Search tab ---
const SearchStack = createNativeStackNavigator();
function SearchStackScreen() {
return (
);
}
// --- Bottom Tab Navigator ---
const Tab = createBottomTabNavigator();
export default function App() {
return (
({
tabBarActiveTintColor: '#e91e63',
tabBarInactiveTintColor: '#gray',
})}
>
/* icon */ }}
/>
/* icon */ }}
/>
{/* Add more tabs as needed */}
);
}
// Dummy screen components
function HomeFeed() { return Home feed
; }
function PostDetail() { return Post detail
; }
function SearchResults() { return Search results
; }
function UserProfile() { return User profile
; }
Each tab’s stack is only mounted when the tab first receives focus, thanks to lazy mounting built into createBottomTabNavigator.
Navigating inside a nested stack
From any screen inside a stack you can use the navigation prop as usual:
// Inside HomeFeed
navigation.navigate('PostDetail', { postId: 42 });
Because the navigator hierarchy is scoped, the action stays within the Home stack and does not affect other tabs.
Deep linking with nested navigators
To support a URI like myapp://search/user/123 that opens the Profile screen inside the Search tab, configure linking prefixes for each navigator:
const linking = {
prefixes: ['myapp://'],
config: {
screens: {
Home: 'home',
Search: {
screens: {
SearchResults: 'search',
UserProfile: 'search/user/:userId',
},
},
},
},
};
<NavigationContainer linking={linking}>...</NavigationContainer>
When the link is tapped, React Navigation resolves the path to the nested screen and automatically focuses the Search tab.
Trade‑offs and limitations
- Complexity: Deep nesting (e.g., tabs inside drawers inside stacks) makes the navigation state harder to inspect with tools like React DevTools.
- TypeScript: You must annotate navigation props with the appropriate generic, e.g.,
NativeStackProp<ParamList>, to avoidanywarnings. - Performance on low‑end devices: While lazy mounting reduces initial render cost, each tab’s stack still mounts its own set of screens when first opened, which can increase memory usage if many heavy screens are present.
To verify that the setup works, run the app on an emulator or device, switch between tabs, navigate to a nested screen, and confirm the header reflects the stack’s options. Then test a deep link via adb shell am start -W -a android.intent.action.VIEW -d "myapp://search/user/7" com.yourapp (Android) or the equivalent Xcode command for iOS and ensure the correct screen appears with the tab bar still visible.
Actionable closing
Start with a simple bottom tab navigator, add a stack per tab, and gradually introduce nested screens as needed. Keep the linking configuration in sync with your navigator hierarchy, and use TypeScript generics to catch mismatched param lists early. This pattern gives you per‑tab navigation independence without sacrificing a consistent tab bar.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.