Tutorial: Building ExpenseApp with React Native and Expo Chapter 1: Web, HTML, CSS & JavaScript Fundamentals Chapter 2: Orientation & Environment Setup Chapter 3: JSX & Core Components Chapter 4: State, Props & Component Architecture Chapter 5: Lists & Layout with Flexbox Chapter 6: Inputs & Form Handling Chapter 7: React Native Tutorial – StickerSmash with Expo Chapter 8: Advanced JavaScript for React Native Chapter 9: Form Handling & Validation with Formik and Yup Chapter 10: Navigation with Expo Router Chapter 11: Navigation Params & Modals Chapter 12: React Hooks, Side Effects & Data Flow Chapter 13: Global State Management with Context API & AsyncStorage Chapter 14: API Integration, Async Operations & Error Handling Chapter 15: Dynamic Bottom Tabs & Drawer Navigation Chapter 16: Advanced JavaScript – Async Patterns Chapter 17: Device APIs – Camera, Image Picker & Location Chapter 18: Notifications & Permissions Chapter 19: Animations & Microinteractions Chapter 20: Performance Optimization Chapter 21: TypeScript Fundamentals for React Native Chapter 22: Theme Context & Dark Mode Chapter 23: Advanced TypeScript – Navigation Types & API Typings Chapter 24: Expo Router Authentication & Transaction Module Chapter 25: Testing Fundamentals (Jest & React Native Testing Library) Chapter 26: Debugging & Troubleshooting Chapter 27: Code Quality & Refactoring Chapter 28: Build & Deployment with Expo (EAS) Chapter 29: Capstone Build-out – Features, Polish & Production Readiness Chapter 30: Capstone Demo, Final Review & Next Steps

Chapter 11: Navigation Params & Modals


In this chapter, we enhance the navigation flow of ExpenseApp by:

  • Passing data between screens
  • Reading route parameters
  • Opening popup screens using modals

These concepts are commonly used in real-world applications.

For example:

  • Opening expense details
  • Editing an expense
  • Opening forms in popup mode
  • Passing selected item information between screens

We will extend the existing ExpenseApp screens created in previous chapters such as:

  • index.tsx
  • add-expense.tsx
  • categories.tsx
  • settings.tsx

🎯 What You Will Learn

By the end of this chapter, you will:

  • Pass data between screens
  • Use route parameters with Expo Router
  • Read params using useLocalSearchParams
  • Handle optional parameters safely
  • Implement modal screens
  • Open and close modals
  • Pass params to modal screens

🔄 Why Navigation Params?

Screens often need to share data.

For example:

Expense List → Expense Details

When a user taps an expense item, we pass the selected expense information to another screen.


📦 Existing ExpenseApp Flow

We already created:

(tabs)
├── index.tsx
├── add-expense.tsx

(drawer)
├── categories.tsx
├── settings.tsx

Now we will add:

expense-details.tsx
add-expense-modal.tsx

📤 Passing Params Between Screens

Expo Router allows passing params using:

router.push();

Update src/app/(tabs)/index.tsx

We will now display a small expense list and navigate to details screen.

import React from "react";

import { FlatList, Pressable, Text, View } from "react-native";

import { router } from "expo-router";

import { style } from "../../styles/styles";

const expenses = [
  {
    id: 1,
    description: "Food",
    amount: 250,
  },

  {
    id: 2,
    description: "Travel",
    amount: 1200,
  },

  {
    id: 3,
    description: "Shopping",
    amount: 800,
  },
];

export default function DashboardScreen() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Expense Dashboard</Text>

      <FlatList
        data={expenses}
        keyExtractor={(item) => item.id.toString()}
        renderItem={({ item }) => (
          <Pressable
            style={styles.card}
            onPress={() =>
              router.push({
                pathname: "/expense-details",

                params: {
                  id: item.id,
                  description: item.description,
                  amount: item.amount,
                },
              })
            }
          >
            <Text style={styles.description}>{item.description}</Text>

            <Text style={styles.amount}>₹{item.amount}</Text>
          </Pressable>
        )}
      />

      <Pressable
        style={styles.button}
        onPress={() => router.push("/add-expense-modal")}
      >
        <Text style={styles.buttonText}>Add Expense</Text>
      </Pressable>
    </View>
  );
}

🔹 Understanding router.push()

router.push({
  pathname: "/expense-details",

  params: {
    id: item.id,
    description: item.description,
    amount: item.amount,
  },
});

👉 Navigates to another screen

👉 Passes data along with navigation


📥 Receiving Params

Create:

📁 src/app/expense-details.tsx

import { Text, View } from "react-native";

import { useLocalSearchParams } from "expo-router";

import { style } from "../styles/styles";

export default function ExpenseDetailsScreen() {
  const { id, description, amount } = useLocalSearchParams();

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Expense Details</Text>

      <Text style={styles.detailsText}>Expense ID: {id}</Text>

      <Text style={styles.detailsText}>Description: {description}</Text>

      <Text style={styles.detailsText}>Amount: ₹{amount}</Text>
    </View>
  );
}

🔹 What is useLocalSearchParams()?

useLocalSearchParams() reads values passed during navigation.


Example

const { id, description, amount } = useLocalSearchParams();

👉 Similar to query parameters in web applications.


⚠️ Handling Optional Params Safely

Sometimes params may be missing.


Safe Example

const { description = "No Description", amount = 0 } = useLocalSearchParams();

Why Important?

Prevents:

  • Undefined errors
  • Crashing UI
  • Invalid rendering

✅ Best Practice: Pass Minimal Data

Instead of passing entire objects:

params: {
  id: item.id;
}

Later, the screen can fetch complete data using API/database.


Why Better?

  • Smaller navigation payload
  • Cleaner architecture
  • Fresh data fetching
  • Better scalability

🪟 Understanding Modals

A modal is a popup-like screen shown on top of the current screen.


Common Use Cases

  • Add Expense form
  • Quick editing
  • Confirm dialogs
  • Filters
  • Settings popup

📁 Create Modal Screen

Create:

📁 src/app/add-expense-modal.tsx


Reusing Previous add-expense.tsx

Instead of creating a new form, we reuse the existing Add Expense screen created earlier.

import AddExpense from "./(tabs)/add-expense";

export default function AddExpenseModal() {
  return <AddExpense />;
}

Why Reuse Components?

Benefits:

  • Avoids duplication
  • Easier maintenance
  • Consistent UI
  • Better scalability

⚙️ Configure Modal in Root Layout

Update:

📁 src/app/_layout.tsx

import { Stack } from "expo-router";

export default function RootLayout() {
  return (
    <Stack>
      <Stack.Screen
        name="(tabs)"
        options={{
          headerShown: false,
        }}
      />

      <Stack.Screen
        name="(drawer)"
        options={{
          headerShown: false,
        }}
      />

      <Stack.Screen
        name="expense-details"
        options={{
          title: "Expense Details",
        }}
      />

      <Stack.Screen
        name="add-expense-modal"
        options={{
          presentation: "modal",
          title: "Add Expense",
        }}
      />
    </Stack>
  );
}

🔹 What Does presentation: "modal" Mean?

presentation: "modal";

👉 Displays screen as popup modal.


Modal Behavior

Platform Behavior
iOS Slides from bottom
Android Opens as overlay

🚀 Opening Modal

We already added:

router.push("/add-expense-modal");

inside dashboard screen.


🔙 Closing Modal

Expo Router provides:

router.back();

Example

Inside add-expense.tsx:

import { router } from "expo-router";

router.back();

👉 Closes modal and returns to previous screen.


📤 Passing Params to Modal

Modals can also receive params.


Example

router.push({
  pathname: "/add-expense-modal",

  params: {
    id: 1,
  },
});

📥 Reading Params in Modal

const { id } = useLocalSearchParams();

🧪 ExpenseApp Navigation Flow


Primary Navigation (Tabs)

Dashboard
Add Expense

Secondary Navigation (Drawer)

Categories
Settings

Additional Screens

Expense Details
Add Expense Modal

Full Flow

Dashboard
   ↓
Expense Details
   ↓
Edit/Add Expense Modal

🎨 Update styles/styles.ts

Add:

card: {
  backgroundColor: "#fff",
  padding: 15,
  borderRadius: 10,
  marginBottom: 10,
  elevation: 2,
},

detailsText: {
  fontSize: 18,
  marginBottom: 10,
},

⚠️ Common Mistakes


❌ Passing Large Objects

params: {
  expense: item;
}

✅ Better

params: {
  id: item.id;
}

❌ Creating Separate Duplicate Forms

Avoid copying entire Add Expense screen into modal.


✅ Better

Reuse existing component.


❌ Using Modals for Complex Navigation

Modals should remain lightweight.


✅ Best Practices

  • Pass minimal params
  • Validate params safely
  • Reuse existing components
  • Keep modals lightweight
  • Use modals for short interactions
  • Keep navigation structure predictable

📘 Chapter Summary

In this chapter, you:

  • Passed params between screens
  • Used useLocalSearchParams
  • Implemented Expense Details screen
  • Created modal screens
  • Reused existing Add Expense form
  • Opened and closed modals
  • Passed params to modal screens
  • Improved ExpenseApp navigation flow
Need help with your project?
I can help you explore our services or start an inquiry.