| name | mobile-navigation-patterns |
| description | Navigation patterns for mobile apps (stack, tab, modal, drawer, nested flows) with platform conventions across SwiftUI, Compose, Flutter, and React Native. Use when structuring navigation or handling deep links. |
Mobile Navigation Patterns
Instructions
Navigation is the skeleton of a mobile app. Choosing the wrong pattern creates lost users, broken back behavior, and deep-link bugs. Use canonical patterns and respect platform conventions.
1. Primary Navigation Shapes
| Shape | Intent | Notes |
|---|
| Stack | Drill into detail and return | Default on both platforms |
| Tab | 2-5 peer destinations | Bottom on both platforms in 2026 |
| Drawer | Many secondary destinations | Common on Android, rarer on iOS |
| Modal | Self-contained task | Present, cancel, done |
| Nested (tabs of stacks) | App shell | Each tab owns its own stack |
| Wizard | Linear multi-step flow | Explicit progress, cancelable |
2. Stack Navigation
Stack navigation is push/pop. The back button (hardware on Android, swipe/chevron on iOS) must always work predictably.
NavigationStack(path: $path) {
ArticleListView()
.navigationDestination(for: Article.self) { ArticleDetailView(article: $0) }
}
NavHost(navController, startDestination = "list") {
composable("list") { ArticleListScreen(onOpen = { id -> navController.navigate("detail/$id") }) }
composable("detail/{id}") { backStack ->
ArticleDetailScreen(id = backStack.arguments?.getString("id")!!)
}
}
// Flutter go_router
final router = GoRouter(routes: [
GoRoute(path: '/', builder: (_, __) => const ArticleListScreen()),
GoRoute(path: '/article/:id', builder: (_, s) => ArticleDetailScreen(id: s.pathParameters['id']!)),
]);
const Stack = createNativeStackNavigator();
<Stack.Navigator>
<Stack.Screen name="List" component={ArticleList} />
<Stack.Screen name="Detail" component={ArticleDetail} />
</Stack.Navigator>
3. Tab Navigation
Each tab owns its own stack. Switching tabs does not reset the other tabs. Deep links restore state into the correct tab.
- iOS:
TabView, UITabBarController.
- Android:
NavigationBar (Material 3), BottomNavigationView.
- Flutter:
NavigationBar, go_router StatefulShellRoute.
- RN:
@react-navigation/bottom-tabs.
4. Modal Navigation
Use modals for tasks that the user can cancel. They cover the current context and return the user exactly where they were.
- Full-screen modal: long flows (checkout).
- Half-sheet modal: quick input (filter, compose).
- Alert/dialog: destructive confirmation.
Every modal has an explicit dismiss and a save/confirm action. Background taps may dismiss on iOS sheets; prevent data loss by auto-saving drafts.
5. Drawer Navigation
A drawer holds secondary destinations. It is rarely the primary shell on iOS. Prefer tabs for top destinations and drawer for utility (settings, help, sign out) only when tabs are insufficient.
6. Nested Flows
The most common mobile shell is tabs of stacks. A deep link to /orders/42 opens the Orders tab and pushes the order detail. Stateful shell routes in go_router, nested navigators in React Navigation, and stacked NavHosts in Compose all express this.
7. Back Behavior Contract
- Android hardware back must always do the right thing. Intercept only when the screen has unsaved work, and then show a confirmation.
- iOS swipe-back must not break the navigation stack. Do not disable it without reason.
- Root back exits to the home screen on Android and is typically a no-op on iOS.
- Deep-link back should walk a sensible synthetic back stack (e.g., Detail -> List -> Home).
8. Deep Links and Restoration
Navigation routes must be expressible as URLs (or structured intents). That is the foundation for:
- Universal Links / App Links (see
lifecycle/deep-links-universal-links).
- Push notifications opening specific screens.
- State restoration after process death.
Agents should avoid screen-only navigation that cannot be expressed as a route.
9. Transition and Motion
- Stack pushes slide horizontally (platform default).
- Tab switches do not animate content.
- Modal present slides up (iOS) or fades (Android Material).
- Do not custom-animate without a product reason; it breaks the muscle memory of the platform.
10. Anti-Patterns
- Hidden navigation (gestures with no visible entry).
- Drawers that hide the primary app function.
- Tabs that change based on state (the bar shape flickers).
- Modals stacked three deep.
- Back button that navigates forward.
- Deep links that do not establish a back stack.
11. Cross-Platform Adaptive Navigation
Use adaptive navigation helpers so the same code produces platform-correct chrome.
// Flutter adaptive
PlatformScaffold(
appBar: PlatformAppBar(title: const Text('Home')),
body: const HomeBody(),
);
<Stack.Screen
name="Home"
component={Home}
options={Platform.select({
ios: { headerLargeTitle: true },
android: { headerTitleAlign: 'left' },
})}
/>
Checklist