Skip to content

Navigation Re-imagined: Mastering Expo Router - Advanced Routing

Posted on:May 6, 2025

Welcome to the Part 2 of Navigation Re-imagined: Mastering Expo Router, Developer đź‘‹

If you missed it, in Part 1 we covered:

Navigation can feel like solving a maze, but with Expo Router it becomes more like following clear signposts. In this part, we’ll add stacks, modals, and route groups, and explain every piece so that even non-technical readers can follow along.

Let’s continue our journey to make navigation simple and understandable!


Table of Contents

Open Table of Contents

Setting things up

For a quick start, make sure you have the Student Mate GitHub repository cloned and ready. You need to check out the branch post/navigation-expo-router-part-1. We’ll be building directly on top of it.

If you haven’t yet installed required packages:

cd student-mate
npm install

Stack Navigation Inside Tabs

What is Expo Router’s Stack feature?

A Stack is like a deck of cards: you place new screens on top and pop them off to go back. This pattern is great for drilling into details while keeping your tab context intact.

Think of your app as a book, developer. Tabs are the main chapters (like “Classes”), and stacks are the pages inside those chapters. You open a chapter (tab) and flip pages (stack screens) to see more details.

Does that make sense? Allow me to show you.

Folder Structure

app/
└── (tabs)/
    └── classes/
        ├── _layout.tsx   # Defines how the pages inside "Classes" work together
        ├── index.tsx     # The main Classes list
        └── [classId].tsx # A detail page for a specific class

Creating the Layout File

  1. Create the file app/(tabs)/classes/_layout.tsx.
  2. Import the Stack component.
  3. Define your screens inside .
// app/(tabs)/classes/_layout.tsx
import { Stack } from 'expo-router';
 
/**
 * ClassesLayout sets up a Stack navigator:
 * - index.tsx shows the list of classes.
 * - [classId].tsx shows details for one class.
 */
export default function ClassesLayout() {
  return (
    <Stack
      screenOptions={{
        headerStyle: { backgroundColor: '#f0f0f0' },
        headerTintColor: '#333',
      }}
    >
      {/* Main list of classes */}
      <Stack.Screen
        name="index"                          
        options={{ title: 'Your Classes' }}  
      />
      {/* Detail page for a selected class */}
      <Stack.Screen
        name="[classId]"                      
        options={({ route }) => ({
          title: `Class ${route.params.classId}`,
        })}
      />
    </Stack>
  );
}

How it works

  1. <Stack>: Think of this as our stack of pages.
  2. screenOptions: Sets a default look (header background & text color).
  3. <Stack.Screen name="index" />: Connects to index.tsx.
  4. <Stack.Screen name="[classId]" />: Connects to [classId].tsx, using the URL parameter to show which class.

What is a Modal in Expo Router?

Imagine a pop‑up window that floats above your book. That’s a modal — great for quick tasks like adding a new assignment without tearing the reader away from the main content.

Expo Router adds modal support via a special route group: any folder named with parentheses and containing a .tsx file will open as a modal overlay.

Create the Modals Folder

app/
└── (modals)/
    └── add-assignment.tsx  # The Add Assignment pop‑up
// app/(modals)/add-assignment.tsx
import { View, Text, TextInput, Button, StyleSheet } from 'react-native';
import { router } from 'expo-router';
 
/**
 * AddAssignmentModal:
 * - Overlay screen for adding a new assignment.
 * - Part of the navigation flow (not just a UI popup).
 */
export default function AddAssignmentModal() {
  return (
    <View style={styles.modal}>
      <Text style={styles.title}>âž• Add New Assignment</Text>
      <TextInput placeholder="Title" style={styles.input} />
      <TextInput placeholder="Due Date" style={styles.input} />
      <Button
        title="Save & Close"
        onPress={() => router.back()}  // Closes the modal
      />
    </View>
  );
}
 
const styles = StyleSheet.create({
  modal: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: 'rgba(0,0,0,0.5)', // Semi-transparent backdrop
    padding: 20,
  },
  title: { fontSize: 24, color: '#fff', marginBottom: 20 },
  input: {
    backgroundColor: '#fff',
    padding: 10,
    marginBottom: 15,
    borderRadius: 6,
  },
});

Let’s break it down

How to Open the Modal

In app/(tabs)/assignments.tsx, add:

<Button
  title="Add Assignment"
  onPress={() => router.push('/add-assignment')} // Opens our modal
/>

Then start your app:

npm run start

Route Groups for Organization

What are Route Groups?

Route groups let you organize related screens without changing the URL structure—useful for auth flows, overlays, or shared routes. For example, like grouping all login pages in a secret folder.

Auth Flow Example

app/
├── (auth)/
│   ├── login.tsx     # Sign-in screen
│   └── register.tsx  # Sign-up screen
└── ...

On the app/(auth)/login.tsx file:

import { View, Text, Button, StyleSheet } from 'react-native';
import { router } from 'expo-router';
 
export default function Login() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>đź”’ Login to Student Mate</Text>
      <Button
        title="Go to Register"
        onPress={() => router.push('/register')}
      />
    </View>
  );
}
 
const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  title:     { fontSize: 24, marginBottom: 20 },
});

On the row 10 highlighted, check how the router is redirecting to /register without the need of the (auth) route group.

What We Learned in Part 2

  1. Stacks in Tabs: Like pages inside chapters—gives depth without losing the main tab.
  2. Modals: Floating panels for quick tasks—open with router.push, close with router.back.
  3. Route Groups: Invisible folders for organizing code—no impact on URLs.

What’s Next

In Part 3, we’ll learn how to:

The journey of a thousand miles begins with a single step. – Lao Tzu

Keep exploring, stay curious, and happy coding! đź’™