Nested Navigators in React Navigation: Keeping Tab State Independent Without Losing Context
A flat navigator resets history when users switch tabs. Learn how to nest a stack navigator inside a tab navigator in React Navigation 6.x so each tab keeps its own state.
24 Jan 2026, 18:32 UTC

The problem: tabs that forget where you were
Imagine a shopping app. A user opens the Home tab, taps a product, opens a second product from the recommendations, and then switches to the Profile tab to check an order. When they tap Home again, they expect to see the second product still open. If your navigation tree is flat—one stack navigator holding every screen—that expectation breaks. The stack resets to its initial route, and the user loses their place.
This is not a bug in React Navigation; it is a consequence of how a single navigator manages history. A stack navigator treats screens as a last-in, first-out (LIFO) list. When you navigate to a screen that belongs to a different logical section, you are pushing onto the same history. Switching tabs by changing the active route does not preserve the previous branch.
The thesis: tabs are containers, not destinations
The structural fix is to stop treating tabs as screens inside one giant stack. Instead, treat each tab as a container that owns its own navigation state. In React Navigation 6.x, you achieve this by nesting a stack navigator inside a tab navigator. The tab navigator becomes the parent; the stack navigator becomes a child rendered as one of the tab's screens.
Because each child navigator maintains its own state object, switching tabs no longer destroys the history inside another tab. The NavigationContainer at the root still holds the overall tree, but the memory of "where I was" lives inside the child navigator.
Worked example: a stack inside a tab
The following setup uses Expo and React Navigation 6.x. Run these commands in your terminal from the directory where you want the project created. No special permissions are required beyond standard file-system access.
npx create-expo-app nested-nav-demo
cd nested-nav-demo
npx expo install @react-navigation/native @react-navigation/native-stack @react-navigation/bottom-tabs react-native-screens react-native-safe-area-context
Replace nested-nav-demo with your preferred project name. After installation, replace the contents of App.js with a structure similar to this:
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { Text, View } from 'react-native';
const Tab = createBottomTabNavigator();
const HomeStack = createNativeStackNavigator();
function FeedScreen() {
return <View><Text>Feed</Text></View>;
}
function DetailsScreen() {
return <View><Text>Details</Text></View>;
}
function SettingsScreen() {
return <View><Text>Settings</Text></View>;
}
function HomeStackScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="Feed" component={FeedScreen} />
<HomeStack.Screen name="Details" component={DetailsScreen} />
</HomeStack.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
The key detail is that HomeStackScreen is an ordinary React component that returns a navigator. React Navigation treats it as a screen inside the tab navigator, but internally it is a full stack with its own history.
How to verify independent state
You do not need to trust the architecture on faith. Open the app in an emulator or on a device, navigate from Feed to Details, switch to the Settings tab, and then switch back to Home. The Details screen should still be visible.
For a deeper check, attach React DevTools and inspect the NavigationContainer state tree. You should see a nested structure where the Home tab holds its own stack state, separate from the Settings tab. If the stack resets, the nesting is likely misconfigured—most commonly because the stack navigator was rendered outside the tab screen component rather than inside it.
Trade-offs: headers, memory, and nesting depth
Nesting solves the state problem, but it introduces two practical costs.
Header collisions. A tab navigator and a stack navigator can both try to render a header. In React Navigation 6.x with native-stack, headers are rendered by the platform. If the parent tab navigator also has headerShown enabled, you may see duplicate or misaligned headers. The safe default is to let the child stack own the header and disable it on the parent tab navigator, or vice versa. Test the result on both iOS and Android, because native header behavior differs.
Memory and complexity. Every navigator level adds a state object and a set of mounted screens. Deeply nested trees—tabs containing stacks containing tabs—increase memory pressure and make deep linking harder to reason about. For typical apps, keep the hierarchy to two or three levels. If you find yourself nesting a fourth level, consider whether a modal stack or a separate navigation flow would be clearer.
Actionable takeaway
If your app has bottom tabs and any tab requires more than one screen, start by wrapping that tab's screens in a native stack navigator. Do it for one tab first, verify the state isolation with React DevTools, and confirm the header renders correctly on both platforms before replicating the pattern. Nested navigators are not a premature optimization; they are the default structure that matches how users expect tabs to behave.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.