Stack Navigator Inside Bottom Tabs: Practical Guide with React Navigation
Learn how to nest a Stack navigator inside a Bottom Tab navigator in React Navigation, pass parameters safely, and avoid common pitfalls like state preservation and circular imports.
24 Mar 2026, 21:47 UTC

Why Nest a Stack Inside a Bottom Tab?
In many apps you want each tab to have its own navigation stack—e.g., a Home tab that can push to Details, and a Settings tab that can push to Profile. React Navigation makes this trivial by letting you nest a StackNavigator inside each tab screen. The result is isolated navigation history per tab and the ability to pass parameters between stack screens.
Set Up the Project
Run on the command line (project root):
npm install @react-navigation/native @react-navigation/stack @react-navigation/bottom-tabs react-native-screens react-native-safe-area-context
Wrap the root component in NavigationContainer (usually in App.tsx).
Define the Navigators
Separate each navigator into its own component to avoid re‑creation on each render.
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createStackNavigator, StackNavigationProp } from '@react-navigation/stack';
import { RouteProp } from '@react-navigation/native';
import React from 'react';
import HomeScreen from './screens/HomeScreen';
import DetailsScreen from './screens/DetailsScreen';
import SettingsScreen from './screens/SettingsScreen';
import ProfileScreen from './screens/ProfileScreen';
// Stack param list for Home tab
export type HomeStackParamList = {
Home: undefined;
Details: { id: number };
};
// Stack navigator for Home
const HomeStack = createStackNavigator<HomeStackParamList>();
const HomeStackScreen = () => (
);
// Stack param list for Settings tab
export type SettingsStackParamList = {
Settings: undefined;
Profile: { userId: string };
};
// Stack navigator for Settings
const SettingsStack = createStackNavigator<SettingsStackParamList>();
const SettingsStackScreen = () => (
);
// Bottom tab navigator
const Tab = createBottomTabNavigator();
const BottomTabs = () => (
);
Pass and Read Parameters
From HomeScreen push to Details with a parameter:
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
type HomeScreenNavigationProp = NativeStackNavigationProp<HomeStackParamList, 'Home'>;
const HomeScreen = ({ navigation }: { navigation: HomeScreenNavigationProp }) => (
navigation.navigate('Details', { id: 42 })}
/>
);
In DetailsScreen read the param with type safety:
import { RouteProp } from '@react-navigation/native';
type DetailsScreenRouteProp = RouteProp<HomeStackParamList, 'Details'>;
const DetailsScreen = ({ route }: { route: DetailsScreenRouteProp }) => {
const { id } = route.params;
return Details for item {id};
};
Common Pitfalls and How to Avoid Them
- Re‑creating Navigators: Defining a navigator inside a component that re-renders causes a new navigator instance each time. Separate the navigator into its own component or wrap it with
React.memo. - Circular Imports: Importing a screen that itself imports the navigator can create a loop. Keep screens in a flat folder or use lazy loading via
React.lazy. - State Preservation: By default, switching tabs keeps the inner stack state. If you want a fresh stack on focus, set
resetOnBlurtotrueon the tab screen options:
<Tab.Screen
name="HomeTab"
component={HomeStackScreen}
options={{ resetOnBlur: true }}
/>
Testing the Setup
- Start the app with
npm start(orexpo startif using Expo). - Tap the
Hometab, press the button to pushDetails, then switch toSettings. - Return to
Home; theDetailsscreen should still be visible ifresetOnBlurisfalse(default). - Run
tsc --noEmitto ensure TypeScript infers the correct param types.
Limitations
- Deep linking into nested screens requires explicit configuration of
initialRouteNameandscreenOptionsfor each navigator. - Passing large objects between screens can bloat the navigation state; consider using a global store (e.g., Redux) for heavy data.
- Animations differ between stack and tab navigators; custom transitions may need
cardStyleInterpolatoradjustments.
Takeaway
By nesting a StackNavigator inside each tab of a BottomTabNavigator, you get isolated navigation flows, clean param passing, and predictable state. Keep navigators separate, watch for circular imports, and use resetOnBlur if you need fresh stacks on tab switch.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.