Choosing a React Navigation Navigator: Stack, Tabs, or Drawer
Match your navigator to your app's information architecture: stack for hierarchical flows, bottom tabs for three to five top-level sections, and drawer for secondary screens.
10 Jul 2026, 08:54 UTC

Decision: match the navigator to the information architecture
Your React Native app has grown past a single screen, and the team must decide how users move between screens. The wrong navigator choice creates awkward navigation flows, brittle deep links, and difficult TypeScript types. The useful takeaway is simple: choose the navigator based on whether screens are hierarchical or parallel, how many screens exist, and which platform conventions your users expect.
Constraints to settle first:
- Screen count and whether screens belong to one flow or several independent sections.
- User journey: linear drill-down, top-level switching, or occasional access to settings.
- Platform conventions: bottom tabs are common on iOS and Android, while drawers suit dense Android utility menus.
- Whether you need headers, swipe-back gestures, or modal presentation.
Supported navigator options
| Navigator | Best for | Typical screen count | Key behavior | Main trade-off |
|---|---|---|---|---|
| Stack | Hierarchical flows, drill-down screens, modals | Any, usually linear sequences | Push and pop screens; supports headers and iOS swipe-back | Not suited for top-level parallel sections; too many stacks confuse history |
| Bottom tab | Three to five top-level sections with parallel access | 3 to 5 tabs | Fixed tab bar; each tab maintains its own navigation state | Poor fit for many screens or deep hierarchies; visual density suffers on small screens |
| Drawer | Many secondary screens, settings, and utility features | 6 or more secondary destinations | Hidden side menu opened by gesture or button | Lower discoverability than tabs; gesture can conflict with swipeable content |
Trade-offs and nesting limits
A stack navigator is the default for hierarchical content. It manages a history stack, so users can move forward and backward. Headers and titles come built in, and iOS swipe-back works out of the box. Use it for login flows, wizards, and drill-down details.
A bottom tab navigator keeps top-level sections visible and reachable with one tap. It is ergonomic when you have about three to five tabs. Beyond that, the bar becomes crowded and small-screen layouts break. Tabs are less appropriate when one section contains many nested screens, because each tab carries its own stack and the global history becomes harder to reason about.
A drawer navigator hides secondary destinations behind a gesture or menu button. It works well for settings, help, and infrequent actions, but users must discover it. Avoid using a drawer as the primary navigation for core features.
React Navigation lets you nest navigators, such as placing bottom tabs inside a stack. Nesting is acceptable at one level, but deeper nesting complicates deep linking, state persistence, and TypeScript navigation types. Do not exceed two navigator levels.
Concrete implementation: stack with nested bottom tabs
This example assumes React Navigation 6.x. Verify your installed version before copying the snippet, because APIs differ between major versions.
# Run in the project root. No special permissions required.
npm ls @react-navigation/native
Expect output showing @react-navigation/native@6.x.x. If the major version differs, the import and component names below may not match your installed API surface.
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
function HomeScreen() { return null; }
function ProfileScreen() { return null; }
function SettingsScreen() { return null; }
const Tab = createBottomTabNavigator();
function TopTabs() {
return (
<Tab.Navigator>
<Tab.Screen name='Home' component={HomeScreen} />
<Tab.Screen name='Profile' component={ProfileScreen} />
</Tab.Navigator>
);
}
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{ headerShown: true }}>
<Stack.Screen name='Tabs' component={TopTabs} />
<Stack.Screen name='Settings' component={SettingsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
This nests bottom tabs inside a single stack, which stays within the two-level nesting limit. The stack provides the header and the back gesture, while the tabs switch between Home and Profile. For TypeScript, declare a parameter list so navigation props resolve without any casts:
type RootStackParamList = {
Tabs: undefined;
Settings: undefined;
};
Validation checklist
Run these checks after adding the navigator:
- Start the app on an iOS simulator or Android emulator. Confirm the tab bar appears on the Tabs screen and the header appears on both screens.
- Switch between tabs, then open Settings. Use the hardware back button or iOS swipe-back to return. Confirm
navigateandgoBackbehave as expected. - Compile types. Run
npx tsc --noEmitin the project root and verify there are no navigation-related type errors. - Test deep linking by invoking the configured URI scheme. Replace
MYAPP_SCHEMEwith your app's registered scheme:
# Android: device or emulator must be running and reachable via adb.
adb shell am start -W -a android.intent.action.VIEW -d 'MYAPP_SCHEME://profile'
# iOS: simulator must be booted.
xcrun simctl openurl booted 'MYAPP_SCHEME://profile'
A successful check opens the app and mounts the Profile screen without crashes. A failed check usually means the URI scheme is registered only in JavaScript; it must also be declared in the native project, such as AndroidManifest.xml on Android or Info.plist on iOS.
Practical way to check the result: open the app, tap each tab, confirm the correct screen and header appear, trigger a deep link, and confirm the target screen mounts. If TypeScript compiles without any casts and deep links open the right screen, the navigator choice is implemented correctly.
Limitations
Navigator APIs differ between React Navigation major versions, so match examples to your installed version. Bottom tab bars can look cramped on small screens, and drawer discoverability is lower than tabs. Header visibility and gesture behavior vary by navigator and operating system. Deep linking and state persistence require extra native configuration and testing on both platforms. If you find yourself nesting more than two navigators, flatten the structure or reconsider the information architecture.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.