Skip to main content

Module 6: Navigation with Expo Router

Navigation Concepts

Understanding how users move through mobile apps before we build it

๐ŸŽฏ Learning Objectives

  • Understand why mobile navigation differs fundamentally from web navigation
  • Learn the four core navigation patterns: Stack, Tab, Drawer, and Modal
  • Visualize the navigation stack and how screens layer on top of each other
  • Recognize platform-specific navigation conventions for iOS and Android
  • Understand navigation state and how it drives what users see
  • Know why navigation libraries exist and what problems they solve
  • Preview the navigation architecture we'll build throughout this module

Web vs Mobile Navigation

If you're coming from web development, you need to fundamentally rewire how you think about navigation. On the web, navigation is simple: URLs map to pages, the browser handles the back button, and every page exists independently. Mobile is differentโ€”dramatically different.

๐Ÿ“– The Core Difference

Web navigation is URL-based and stateless. Mobile navigation is state-based and hierarchical. On the web, you navigate BY changing the URL. On mobile, you navigate BY manipulating a stack of screens, each maintaining its own state.

Let's visualize this mental shift:

Navigation Mental Models ๐ŸŒ Web Navigation /products/shoes/nike-123 Product Page Each URL = One Page Page is destroyed on navigate State resets unless persisted Browser handles back button Navigate = Change URL Old page gone, new page loads ๐Ÿ“ฑ Mobile Navigation Home Screen Products List Product Detail โ† Visible Screen Screens stack on top Previous screens preserved

This difference has profound implications for how you build apps:

๐Ÿ”„ Key Differences

Aspect Web Mobile
Navigation trigger URL change Stack manipulation
Previous screen Destroyed (usually) Preserved in memory
Screen state Lost unless saved Maintained while in stack
Back button Browser history Pops current screen off stack
Animations Page reload (or SPA fade) Native slide/fade transitions
Deep linking Natural (URLs exist) Requires configuration

๐Ÿ’ก The SPA Connection

If you've built Single Page Applications (SPAs) with React Router, you're closer to mobile navigation than traditional web developers. SPAs also manage navigation state in JavaScript rather than relying solely on the browser. However, even SPAs typically don't preserve previous "pages" the way mobile doesโ€”they mount and unmount components as routes change.

Why Mobile Navigation Is Stateful

Mobile navigation evolved this way for good reasons:

  1. Limited screen real estate: You can only show one "page" at a time, so you need a system to manage what's visible
  2. Touch-based interaction: Gestures like swiping to go back require screens to exist (you're literally dragging the current screen aside)
  3. Memory and performance: Creating/destroying complex UIs is expensive; keeping screens in memory enables instant back navigation
  4. User expectations: When users go back, they expect to see exactly what they left, not a fresh reload

This statefulness is both powerful and challenging. It enables fluid, native-feeling navigation, but it also means you need to think carefully about screen lifecycle, memory usage, and state management.

Stack Navigation: The Foundation

Stack navigation is the most fundamental navigation pattern in mobile apps. Think of it like a deck of cards: you can add cards to the top (push), remove the top card (pop), or see what's on top (the current screen).

๐Ÿ“– Stack Navigation Defined

Stack navigation organizes screens in a last-in-first-out (LIFO) structure. When you navigate to a new screen, it's "pushed" onto the stack. When you go back, the current screen is "popped" off, revealing the screen beneath it.

This is the navigation pattern you encounter constantly:

  • Email app: Inbox โ†’ Email Detail โ†’ Attachment Preview
  • Shopping app: Product List โ†’ Product Detail โ†’ Add to Cart
  • Settings: Settings โ†’ Wi-Fi โ†’ Network Details
Stack Navigation: Push and Pop 1. Initial Home 2. Push "Products" Home Products 3. Push "Detail" Home Products Detail 4. Pop (Go Back) Home Products Detail removed 5. Pop Again Home Current (visible) screen Screen in stack (preserved) Stack grows upward โ†’

Stack Operations in Code

Here's what stack navigation looks like conceptually (we'll see the real Expo Router implementation soon):

// Conceptual stack operations

// 1. Push a new screen onto the stack
navigation.push('ProductDetail', { productId: 123 });
// Stack: [Home, Products, ProductDetail]

// 2. Go back (pop current screen)
navigation.goBack();
// Stack: [Home, Products]

// 3. Pop to a specific screen
navigation.popTo('Home');
// Stack: [Home]

// 4. Replace current screen (no stack change)
navigation.replace('Login');
// Stack: [Login] (if Home was the only screen)

// 5. Reset entire stack
navigation.reset({
  routes: [{ name: 'Home' }]
});
// Stack: [Home] (fresh start)

โœ… Key Stack Behaviors

  • Push: Adds a new screen on top, animates in from the right (iOS) or bottom (Android)
  • Pop: Removes the top screen, animates out, reveals the screen below
  • Replace: Swaps the current screen without adding to history (useful for auth flows)
  • Reset: Clears the entire stack and starts fresh (useful after login/logout)

Screen Lifecycle in a Stack

Understanding when screens mount and unmount is crucial for managing data and effects:

sequenceDiagram
    participant User
    participant Home
    participant Products
    participant Detail
    
    Note over Home: Home mounts (first screen)
    User->>Products: Navigate to Products
    Note over Products: Products mounts
    Note over Home: Home stays mounted (hidden)
    
    User->>Detail: Navigate to Detail
    Note over Detail: Detail mounts
    Note over Products: Products stays mounted (hidden)
    
    User->>Products: Go back
    Note over Detail: Detail unmounts
    Note over Products: Products becomes visible
    
    User->>Home: Go back
    Note over Products: Products unmounts
    Note over Home: Home becomes visible
                

This behavior has important implications:

  • Data fetching: Screens might not refetch when you return to them (they're still mounted)
  • State persistence: Form inputs, scroll positionsโ€”all preserved while in the stack
  • Memory usage: Deep navigation hierarchies keep multiple screens in memory
  • Side effects: Be careful with effects that should only run when the screen is visible

โš ๏ธ Common Gotcha: Stale Data

Since screens stay mounted, you might navigate back to a screen with outdated data. For example, you edit a product's details, go back to the list, and the list still shows old data. You'll need patterns like focus listeners or global state to handle thisโ€”we'll cover solutions later in this module.

Tab Navigation: Parallel Destinations

While stack navigation is hierarchical (one screen leads to another), tab navigation is parallelโ€”multiple top-level destinations that users can switch between freely. Think of it as having multiple stacks, one per tab.

๐Ÿ“– Tab Navigation Defined

Tab navigation provides a persistent bar (usually at the bottom) that lets users switch between distinct sections of your app. Each tab can contain its own navigation stack, allowing independent navigation within each section.

You've used tab navigation in virtually every app:

  • Instagram: Home, Search, Reels, Shop, Profile
  • Spotify: Home, Search, Your Library
  • Phone app: Favorites, Recents, Contacts, Keypad, Voicemail
Tab Navigation: Parallel Stacks Home Feed ๐Ÿ  Home ๐Ÿ” Search ๐Ÿ›’ Cart ๐Ÿ‘ค Profile Each tab has its own stack Home Detail โ† Active Search (preserved) Cart Checkout (preserved) Profile Settings Privacy (preserved) Key Behavior โ€ข Each tab maintains its stack โ€ข Switching tabs preserves state โ€ข Tab bar always visible

Tab Navigation Characteristics

๐Ÿ“‹ Tab Navigation Rules

  • Persistence: Each tab's navigation state is preserved when switching tabs
  • Independence: Navigating within one tab doesn't affect other tabs
  • Visibility: The tab bar typically stays visible (but can be hidden for immersive experiences)
  • Reset behavior: Tapping an already-active tab often scrolls to top or pops to root
  • Badge indicators: Tabs can show badges for notifications (like unread counts)
// Tab navigation mental model

// User is in Home tab, navigates deep
// Stack: { home: [Feed, PostDetail, Comments], search: [], cart: [], profile: [] }

// User switches to Profile tab
// Stack: { home: [Feed, PostDetail, Comments], search: [], cart: [], profile: [Profile] }
// Home tab's stack is preserved!

// User navigates in Profile
// Stack: { home: [Feed, PostDetail, Comments], search: [], cart: [], profile: [Profile, Settings] }

// User switches back to Home
// Stack: { home: [Feed, PostDetail, Comments], search: [], cart: [], profile: [Profile, Settings] }
// They're back exactly where they left off in the Home tab!

๐Ÿ’ก Design Guideline: Tab Count

Both iOS and Android recommend 3-5 tabs maximum. More than 5 tabs creates a cluttered interface and makes targets too small on phone screens. If you have more destinations, consider using a drawer navigation or reorganizing your information architecture.

Drawer Navigation: Hidden Menus

Drawer navigation (also called "hamburger menu" or "side menu") hides navigation options in a panel that slides in from the edge of the screen. It's useful when you have many destinations but don't want them always visible.

๐Ÿ“– Drawer Navigation Defined

Drawer navigation provides a sliding panel (typically from the left) that contains navigation options. It's hidden by default and revealed by tapping a menu icon or swiping from the screen edge.

You'll find drawer navigation in:

  • Gmail (switch between Inbox, Sent, Drafts, labels)
  • Google Drive (switch between My Drive, Shared, Starred)
  • Many Android apps (Android's Material Design emphasizes drawers)
Drawer Navigation Drawer Closed โ˜ฐ Inbox tap โ˜ฐ Drawer Open ๐Ÿ“ฅ Inbox ๐Ÿ“ค Sent ๐Ÿ“ Drafts ๐Ÿ—‘๏ธ Trash โš™๏ธ Settings select New Screen โ˜ฐ Drafts

โš ๏ธ Drawer Navigation Considerations

Drawer navigation has fallen out of favor for primary navigation on iOS (Apple discourages it) and is less common on modern Android apps too. The hidden nature of drawers means lower discoverabilityโ€”users might not know options exist. Consider drawers for secondary navigation or account-switching rather than primary app navigation.

When to Use Drawer Navigation

  • Apps with many sections that users access infrequently
  • Account switching (multiple email accounts, profiles)
  • Settings and secondary features
  • Complex enterprise apps with deep feature sets

Combining Navigation Patterns

Real apps don't use just one navigation patternโ€”they combine multiple patterns to create intuitive, powerful navigation architectures. Understanding how patterns nest is crucial for building complex apps.

flowchart TB
    subgraph App["App Navigation Architecture"]
        subgraph Tabs["Tab Navigator"]
            subgraph HomeStack["Home Stack"]
                H1[Home Feed] --> H2[Post Detail]
                H2 --> H3[Comments]
                H2 --> H4[User Profile]
            end
            
            subgraph SearchStack["Search Stack"]
                S1[Search] --> S2[Results]
                S2 --> S3[Item Detail]
            end
            
            subgraph ProfileStack["Profile Stack"]
                P1[My Profile] --> P2[Edit Profile]
                P1 --> P3[Settings]
                P3 --> P4[Privacy]
            end
        end
        
        Modal1[Compose Post Modal]
        Modal2[Login Modal]
    end
    
    HomeStack -.-> Modal1
    SearchStack -.-> Modal1
    ProfileStack -.-> Modal2
    
    style Modal1 fill:#fff3cd,stroke:#ffc107
    style Modal2 fill:#fff3cd,stroke:#ffc107
                

In this architecture:

  • Tabs provide the top-level navigation structure
  • Stacks within each tab handle hierarchical navigation
  • Modals can be triggered from anywhere for specific tasks

Nesting Rules

๐Ÿ“‹ How Navigators Nest

  • Tabs containing Stacks: Most common pattern. Each tab has its own navigation history.
  • Stack containing Tabs: Less common, but used when tabs should be "inside" the app (e.g., after login).
  • Drawer containing Tabs: Enterprise apps with many features.
  • Modals overlay everything: They sit above all other navigators.
Common Navigation Architectures Most Common Tabs โ†’ Stacks ๐Ÿ  ๐Ÿ” ๐Ÿ‘ค Home Detail Comments Auth Pattern Stack โ†’ (Auth or Tabs) Root Stack Login Signup Tabs (main app) Logged Out Logged In Full Pattern Tabs + Stacks + Modals Tab Bar + Stacks Current Stack Screen Content Modal Overlays everything โœ•

๐Ÿ’ก Authentication Pattern

A very common pattern is having a root stack that conditionally renders either authentication screens (Login, Signup, Forgot Password) or the main app (usually tabs with stacks). When the user logs in/out, the entire navigation tree changes. We'll implement this exact pattern in Lesson 6: Authentication Flows.

Platform Conventions

iOS and Android have different navigation conventions that users expect. A well-built app respects these conventions, providing a native feel on each platform.

Platform Navigation Conventions iOS Conventions โ† Back Title Edit ๐Ÿ  Home ๐Ÿ” Search ๐Ÿ‘ค Profile Large title collapses on scroll Tab bar at bottom always Android Conventions โ† Title โ‹ฎ + ๐Ÿ  Home ๐Ÿ” Search ๐Ÿ‘ค Profile Material You top app bar FAB for primary action

๐Ÿ“‹ Key Platform Differences

Feature iOS Android
Back navigation Swipe from left edge Hardware/gesture back button
Stack animation Slide from right Fade or slide up
Modal animation Slide up as card Fade in or slide up
Tab bar style Icons + labels, translucent Material style, solid
Header style Large titles that collapse Fixed app bar height
Primary action Text button in nav bar FAB (Floating Action Button)

The good news: Expo Router and React Navigation automatically apply platform-appropriate animations and styles. You don't need to manually code different behaviorsโ€”they're built in.

โš ๏ธ Don't Fight Platform Conventions

Resist the temptation to create a "unified" experience that ignores platform norms. iOS users expect to swipe back; Android users expect the system back button to work. Fighting these expectations creates confusion and frustration.

Platform-Specific Navigation Code

import { Platform } from 'react-native';

// Adjusting behavior per platform
const screenOptions = {
  // iOS: slide from right, Android: fade
  animation: Platform.select({
    ios: 'slide_from_right',
    android: 'fade',
  }),
  
  // iOS: large titles, Android: standard
  headerLargeTitle: Platform.OS === 'ios',
  
  // iOS: translucent header, Android: solid
  headerTranslucent: Platform.OS === 'ios',
};

// Handling Android back button
import { BackHandler } from 'react-native';
import { useFocusEffect } from '@react-navigation/native';

function MyScreen() {
  useFocusEffect(
    useCallback(() => {
      const onBackPress = () => {
        // Custom back behavior
        // Return true to prevent default behavior
        return false;
      };

      // Only needed on Android
      if (Platform.OS === 'android') {
        BackHandler.addEventListener('hardwareBackPress', onBackPress);
        return () => BackHandler.removeEventListener('hardwareBackPress', onBackPress);
      }
    }, [])
  );
}

Why Navigation Libraries?

You might wonder: if React Native provides basic views and can respond to touches, why do we need a separate navigation library? Can't we just manage our own stack of screens?

You could build navigation from scratch, but you'd be reinventing a very complex wheel:

๐Ÿšซ What You'd Have to Build Yourself

  • Screen lifecycle management (mount, focus, blur, unmount)
  • Gesture handling for back swipe (iOS) and edge swipe
  • Platform-specific animations with spring physics
  • Hardware back button handling (Android)
  • Deep link URL parsing and state hydration
  • Tab state preservation across switches
  • Modal presentation with proper layering
  • Screen transition interruption handling
  • Accessibility announcements for screen changes
  • Safe area handling in headers and tab bars
  • Keyboard avoiding behavior during navigation
  • State persistence across app restarts

Navigation libraries solve all of this. They've been battle-tested in millions of apps and handle edge cases you'd never think of.

The React Native Navigation Landscape

Two main options dominate React Native navigation:

๐Ÿงญ React Navigation

The most popular navigation library for React Native. It's JavaScript-based, highly customizable, and works with Expo out of the box.

  • Mature ecosystem with many navigators (stack, tabs, drawer, etc.)
  • Deep linking support
  • TypeScript support
  • Active community and documentation

๐Ÿš€ Expo Router (Built on React Navigation)

A file-based router built on top of React Navigation. If you've used Next.js, you'll feel right at home.

  • File-based routing: Create a file, get a route automatically
  • Automatic deep linking: URLs derived from file structure
  • TypeScript routes: Type-safe navigation
  • Universal links: Works on iOS, Android, AND web
  • Built on React Navigation: All its power, better DX

In this course, we'll use Expo Router because:

  1. It provides a more intuitive mental model (files = routes)
  2. Deep linking works automatically
  3. TypeScript integration is excellent
  4. It's the recommended approach for new Expo projects
  5. Understanding it also teaches you React Navigation concepts
flowchart TB
    subgraph ExpoRouter["Expo Router"]
        direction TB
        ER[File-Based API]
        ER --> |"Provides"| DX[Better Developer Experience]
        ER --> |"Generates"| DL[Automatic Deep Links]
        ER --> |"Enables"| TS[Type-Safe Navigation]
    end
    
    subgraph ReactNav["React Navigation"]
        direction TB
        RN[Core Navigation Engine]
        RN --> |"Provides"| Stack[Stack Navigator]
        RN --> |"Provides"| Tabs[Tab Navigator]
        RN --> |"Provides"| Drawer[Drawer Navigator]
        RN --> |"Handles"| Gestures[Gestures & Animations]
    end
    
    ExpoRouter --> |"Built On"| ReactNav
    
    style ExpoRouter fill:#e3f2fd,stroke:#2196F3
    style ReactNav fill:#e8f5e9,stroke:#4CAF50
                

Preview: Expo Router

Before we dive into implementation in the next lesson, let's preview what navigation looks like with Expo Router. The core idea is brilliantly simple: your file structure IS your route structure.

app/
โ”œโ”€โ”€ _layout.tsx          # Root layout (defines navigators)
โ”œโ”€โ”€ index.tsx            # "/" - Home screen
โ”œโ”€โ”€ about.tsx            # "/about" - About screen
โ”œโ”€โ”€ (tabs)/              # Tab group
โ”‚   โ”œโ”€โ”€ _layout.tsx      # Tab navigator configuration
โ”‚   โ”œโ”€โ”€ index.tsx        # "/tabs" or first tab
โ”‚   โ”œโ”€โ”€ search.tsx       # "/tabs/search"
โ”‚   โ””โ”€โ”€ profile.tsx      # "/tabs/profile"
โ”œโ”€โ”€ product/
โ”‚   โ”œโ”€โ”€ _layout.tsx      # Stack for product screens
โ”‚   โ””โ”€โ”€ [id].tsx         # "/product/123" - Dynamic route
โ””โ”€โ”€ (auth)/              # Auth group (parentheses = not in URL)
    โ”œโ”€โ”€ _layout.tsx      # Auth stack
    โ”œโ”€โ”€ login.tsx        # "/login"
    โ””โ”€โ”€ signup.tsx       # "/signup"

Each file automatically becomes a route. No configuration needed. Let's see what the code looks like:

// app/_layout.tsx - Root layout
import { Stack } from 'expo-router';

export default function RootLayout() {
  return (
    <Stack>
      <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      <Stack.Screen name="(auth)" options={{ headerShown: false }} />
      <Stack.Screen name="product/[id]" options={{ title: 'Product' }} />
    </Stack>
  );
}

// app/(tabs)/_layout.tsx - Tab navigator
import { Tabs } from 'expo-router';

export default function TabLayout() {
  return (
    <Tabs>
      <Tabs.Screen 
        name="index" 
        options={{ 
          title: 'Home',
          tabBarIcon: ({ color }) => <Ionicons name="home" color={color} />
        }} 
      />
      <Tabs.Screen 
        name="search"
        options={{
          title: 'Search',
          tabBarIcon: ({ color }) => <Ionicons name="search" color={color} />
        }}
      />
      <Tabs.Screen
        name="profile"
        options={{
          title: 'Profile',
          tabBarIcon: ({ color }) => <Ionicons name="person" color={color} />
        }}
      />
    </Tabs>
  );
}

// app/(tabs)/index.tsx - Home screen
import { View, Text, Pressable } from 'react-native';
import { Link } from 'expo-router';

export default function HomeScreen() {
  return (
    <View>
      <Text>Welcome Home!</Text>
      
      {/* Navigate using Link component */}
      <Link href="/product/123">
        View Product 123
      </Link>
      
      {/* Or with more control */}
      <Link href="/search" asChild>
        <Pressable>
          <Text>Go to Search</Text>
        </Pressable>
      </Link>
    </View>
  );
}

// app/product/[id].tsx - Dynamic route
import { useLocalSearchParams } from 'expo-router';

export default function ProductScreen() {
  const { id } = useLocalSearchParams();
  
  return (
    <View>
      <Text>Product ID: {id}</Text>
    </View>
  );
}

โœ… What Expo Router Gives You

  • Automatic routes: Create a file, get a route
  • Automatic deep linking: /product/123 works immediately
  • Type-safe navigation: TypeScript knows your routes
  • Web support: Same code works on web with proper URLs
  • Nested layouts: _layout.tsx files define structure
  • Groups: Parentheses (auth) organize without affecting URLs

What We'll Build in This Module

Over the next seven lessons, you'll build a complete navigation system:

flowchart TB
    subgraph Module6["Module 6: Navigation with Expo Router"]
        L1[Lesson 1: Concepts] --> L2[Lesson 2: Setup]
        L2 --> L3[Lesson 3: Stack Navigation]
        L3 --> L4[Lesson 4: Tab Navigation]
        L4 --> L5[Lesson 5: Nested Navigation]
        L5 --> L6[Lesson 6: Auth Flows]
        L6 --> L7[Lesson 7: Deep Linking]
        L7 --> L8[Lesson 8: Advanced Patterns]
    end
    
    L8 --> Project[Module Project:
Multi-tab app with auth,
protected routes, deep links] style L1 fill:#4CAF50,color:#fff style Project fill:#ff9800,color:#fff

By the end of this module, you'll have built an app with:

  • Tab-based main navigation
  • Stack navigation within each tab
  • Authentication flow with protected routes
  • Dynamic routes for data-driven screens
  • Working deep links that open specific screens
  • Modal presentations for focused tasks

Hands-On Exercises

These exercises help solidify the navigation concepts before we start implementing.

Exercise 1: Navigation Pattern Identification

Open three of your favorite apps and identify the navigation patterns they use.

For each app, document:

  • What type of primary navigation? (tabs, drawer, single stack)
  • How many levels deep can you navigate?
  • When do modals appear vs stack screens?
  • What happens when you tap an already-active tab?
  • How does the back gesture/button work?
โœ… Example Analysis: Instagram

Primary navigation: Bottom tabs (5 tabs: Home, Search, Reels, Shop, Profile)

Depth: Deep stacks within each tab

  • Home โ†’ Post โ†’ Comments โ†’ User Profile โ†’ Their Posts โ†’ ...
  • Can go many levels deep

Modals used for:

  • Creating new posts (full-screen modal with camera/gallery)
  • Stories viewer
  • Share sheet
  • Comments (sometimes as bottom sheet)

Tab tap behavior:

  • If at root: Scrolls to top
  • If deep in stack: Pops to root

Back behavior:

  • iOS: Swipe from left edge
  • Android: System back button
  • Both: Back arrow in header

Exercise 2: Design a Navigation Architecture

Design the navigation structure for a recipe app with these features:

  • Browse recipes by category
  • Search recipes
  • View recipe details with ingredients and steps
  • Save favorite recipes
  • Create and edit your own recipes
  • User profile with settings
  • Shopping list feature

Create:

  1. A diagram showing the navigation structure
  2. Decision explanations: why tabs vs stack vs modal for each section
  3. The file structure if using Expo Router
โœ… Solution

Navigation Structure:

Root Stack
โ”œโ”€โ”€ (tabs) - Bottom Tab Navigator
โ”‚   โ”œโ”€โ”€ Home Stack
โ”‚   โ”‚   โ”œโ”€โ”€ index (Home/Featured)
โ”‚   โ”‚   โ”œโ”€โ”€ category/[id] (Category listing)
โ”‚   โ”‚   โ””โ”€โ”€ recipe/[id] (Recipe detail)
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ Search Stack  
โ”‚   โ”‚   โ”œโ”€โ”€ index (Search screen)
โ”‚   โ”‚   โ””โ”€โ”€ recipe/[id] (Recipe detail)
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ Favorites Stack
โ”‚   โ”‚   โ”œโ”€โ”€ index (Saved recipes list)
โ”‚   โ”‚   โ””โ”€โ”€ recipe/[id] (Recipe detail)
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ Shopping Stack
โ”‚   โ”‚   โ””โ”€โ”€ index (Shopping list)
โ”‚   โ”‚
โ”‚   โ””โ”€โ”€ Profile Stack
โ”‚       โ”œโ”€โ”€ index (Profile)
โ”‚       โ”œโ”€โ”€ my-recipes (My recipes list)
โ”‚       โ”œโ”€โ”€ recipe/[id] (View my recipe)
โ”‚       โ””โ”€โ”€ settings (Settings)
โ”‚
โ”œโ”€โ”€ (modals) - Modal group
โ”‚   โ”œโ”€โ”€ create-recipe (Full-screen modal)
โ”‚   โ”œโ”€โ”€ edit-recipe/[id] (Full-screen modal)
โ”‚   โ””โ”€โ”€ add-to-list (Bottom sheet modal)
โ”‚
โ””โ”€โ”€ (auth) - Auth group (when logged out)
    โ”œโ”€โ”€ login
    โ””โ”€โ”€ signup

Design Decisions:

  • Tabs: 5 main areas (Home, Search, Favorites, Shopping, Profile) - all frequently accessed
  • Stacks: Within each tab for drilling into content
  • Modals: Creating/editing recipes (new content, can be canceled), adding to shopping list (quick action)
  • Recipe detail shared: Same screen accessible from multiple tabs

File Structure:

app/
โ”œโ”€โ”€ _layout.tsx
โ”œโ”€โ”€ (tabs)/
โ”‚   โ”œโ”€โ”€ _layout.tsx
โ”‚   โ”œโ”€โ”€ index.tsx
โ”‚   โ”œโ”€โ”€ search.tsx
โ”‚   โ”œโ”€โ”€ favorites.tsx
โ”‚   โ”œโ”€โ”€ shopping.tsx
โ”‚   โ””โ”€โ”€ profile/
โ”‚       โ”œโ”€โ”€ _layout.tsx
โ”‚       โ”œโ”€โ”€ index.tsx
โ”‚       โ”œโ”€โ”€ my-recipes.tsx
โ”‚       โ””โ”€โ”€ settings.tsx
โ”œโ”€โ”€ category/
โ”‚   โ””โ”€โ”€ [id].tsx
โ”œโ”€โ”€ recipe/
โ”‚   โ””โ”€โ”€ [id].tsx
โ”œโ”€โ”€ create-recipe.tsx
โ”œโ”€โ”€ edit-recipe/
โ”‚   โ””โ”€โ”€ [id].tsx
โ””โ”€โ”€ (auth)/
    โ”œโ”€โ”€ _layout.tsx
    โ”œโ”€โ”€ login.tsx
    โ””โ”€โ”€ signup.tsx

Exercise 3: Navigation State Visualization

Given this navigation state, describe what the user would see:

{
  "type": "tab",
  "index": 2,
  "routes": [
    {
      "name": "HomeTab",
      "state": {
        "type": "stack",
        "index": 0,
        "routes": [{ "name": "Home" }]
      }
    },
    {
      "name": "SearchTab",
      "state": {
        "type": "stack",
        "index": 1,
        "routes": [
          { "name": "Search" },
          { "name": "Results", "params": { "query": "pizza" } }
        ]
      }
    },
    {
      "name": "ProfileTab",
      "state": {
        "type": "stack",
        "index": 2,
        "routes": [
          { "name": "Profile" },
          { "name": "Settings" },
          { "name": "Privacy" }
        ]
      }
    }
  ]
}

Answer these questions:

  1. Which tab is currently active?
  2. What screen is the user viewing?
  3. What happens if the user taps "Back"?
  4. What happens if the user taps the "Search" tab?
  5. What happens if the user taps the "Home" tab?
โœ… Answers
  1. Active tab: ProfileTab (index: 2)
  2. Current screen: Privacy (it's at index 2 of the Profile stack)
  3. If user taps Back: They'll see Settings (pop Privacy off the stack)
  4. If user taps Search tab: They'll see the Results screen with "pizza" query (the Search tab's stack is preserved at index 1)
  5. If user taps Home tab: They'll see the Home screen (Home tab is at its root)

Key insight: Each tab preserves its navigation state. When the user returns to a tab, they're exactly where they left off.

Exercise 4: Platform Convention Matching

Match these behaviors to the correct platform (iOS, Android, or Both):

  1. Swipe from left edge to go back
  2. Hardware back button
  3. Tab bar at bottom of screen
  4. Large collapsible titles
  5. Floating Action Button (FAB)
  6. Pull to refresh
  7. Modal slides up from bottom
  8. Drawer activated by hamburger menu
โœ… Answers
  1. Swipe from left edge: iOS (though Android now has similar gesture navigation)
  2. Hardware back button: Android
  3. Tab bar at bottom: Both (but more emphasized on iOS)
  4. Large collapsible titles: iOS (iOS 11+ feature)
  5. Floating Action Button: Android (Material Design pattern)
  6. Pull to refresh: Both
  7. Modal slides up: Both (standard on iOS, common on Android)
  8. Drawer/hamburger: Both, but more common on Android

Summary

You now have a solid conceptual foundation for mobile navigation. Before writing any code, you understand the patterns, the state model, and the platform conventions that make mobile navigation work.

๐ŸŽฏ Key Takeaways

  • Mobile navigation is state-based: Screens stack and persist, unlike web pages
  • Four core patterns: Stack (hierarchical), Tabs (parallel), Drawer (hidden menu), Modal (focused task)
  • Patterns combine: Real apps nest navigators (tabs containing stacks, modals overlaying everything)
  • Navigation state is the source of truth: A serializable object that determines what renders
  • Platform conventions matter: iOS and Android users have different expectations
  • Libraries solve complexity: Gestures, animations, deep linkingโ€”don't reinvent these
  • Expo Router = file-based routing: Files become routes with automatic deep linking

๐Ÿงญ The Navigation Mental Model

Think of your app as a building: Tabs are floors (parallel destinations), Stacks are hallways (sequences of rooms), and Modals are popup offices (focused spaces that demand attention before you can leave). Users move through this building, and the navigation state is the map showing exactly where they are.

What's Next?

In the next lesson, we'll set up Expo Router in a new project and explore how the file-based routing system works in practice. You'll create your first routes, navigate between screens, and see how Expo Router handles the complexity we discussed today.

Get ready to turn these concepts into working code!