Codecademy Logo

Navigation

Related learning

  • Build mobile apps with TypeScript and React, using Expo and React Native
    • Intermediate.
      12 hours

The React Navigation Library

React Navigation is a powerful navigation library for React Native that delivers native-like navigation patterns through customizable stack, tab, and drawer navigators.

import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
const Stack = createNativeStackNavigator();
function HomeScreen() {
return (
// Home screen component
);
}
function DetailsScreen() {
return (
// Details screen component
);
}
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;

Expo Router

Expo Router streamlines React Native navigation through file-based routing. It automatically generates routes from the application’s file structure while leveraging React Navigation’s powerful navigation primitives.

// File structure automatically creates routes:
// app/
// index.ts → "/" (home screen)
// profile.ts → "/profile"
// settings/
// index.ts → "/settings"
// notifications.ts → "/settings/notifications"
// app/index.ts (Home Screen)
import { Link } from 'expo-router';
import { View, Text } from 'react-native';
export default function Home() {
return (
<View>
<Text>Welcome Home!</Text>
<Link href="/profile">Go to Profile</Link>
<Link href="/settings">Go to Settings</Link>
</View>
);
}
// app/profile.ts (Profile Screen)
import { Text } from 'react-native';
export default function Profile() {
return <Text>User Profile</Text>;
}

React Navigation Types

React Navigation provides NativeStack for hierarchical screen navigation (like drilling down from a list to details), BottomTabs for organizing top-level app sections (like Home, Search, Profile), and Drawer for sidebar menus with secondary navigation options, covering the essential navigation patterns that create intuitive mobile user experiences.

// NativeStack: Hierarchical navigation (List → Details)
const Stack = createNativeStackNavigator();
<Stack.Navigator>
<Stack.Screen name="List" component={ListScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
// BottomTabs: Top-level app sections
const Tab = createBottomTabNavigator();
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
// Drawer: Sidebar menu
const Drawer = createDrawerNavigator();
<Drawer.Navigator>
<Drawer.Screen name="Main" component={MainTabs} />
<Drawer.Screen name="Settings" component={SettingsScreen} />
</Drawer.Navigator>

Using <NavigationContainer> in Navigation

The <NavigationContainer> wraps the entire app to enable navigation functionality, serving as the foundation that makes navigation props and hooks available to all screens.

import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
// Now all screens have access to navigation
function HomeScreen({ navigation }) {
return (
<Button
title="Go to Profile"
onPress={() => navigation.navigate('Profile')}
/>
);
}

Navigator Creation Pattern

Navigator packages in React Navigation follow a consistent pattern: import the package (like @react-navigation/native-stack), call its creation function (createNativeStackNavigator()), and receive a navigator object with <Navigator> and <Screen> components.

// 1. Import the package
import { createNativeStackNavigator } from '@react-navigation/native-stack';
// 2. Call the creation function
const Stack = createNativeStackNavigator();
// 3. Use the navigator object (contains Navigator and Screen components)
function App() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
);
}

The create[Type]Navigator Method

The navigator object returned by create[Type]Navigator() exports <Navigator> (which handles navigation state, screen rendering, and transition animations) and <Screen> (which defines route configurations including component mapping, titles, and navigation options).

import { createNativeStackNavigator } from '@react-navigation/native-stack';
const Stack = createNativeStackNavigator();
function App() {
return (
// Navigator: Manages navigation state, transitions, and rendering
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerStyle: { backgroundColor: '#f4511e' },
animation: 'slide_from_right'
}}
>
{/* Screen: Defines individual routes with names and components */}
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Welcome Home' }}
/>
<Stack.Screen
name="Profile"
component={ProfileScreen}
options={{
title: 'User Profile',
headerRight: () => <EditButton />
}}
/>
</Stack.Navigator>
);
}

The navigation Object

The navigation object in React Navigation becomes available to screens via props or the useNavigation() hook, providing methods to navigate between screens, go back, and manipulate the navigation state programmatically.

// Method 1: Via props (screen components)
function HomeScreen({ navigation }: { navigation: any }) {
return (
<Button
title="Go to Profile"
onPress={() => navigation.navigate('Profile')}
/>
);
}
// Method 2: Via useNavigation hook (any component)
function BackButton() {
const navigation = useNavigation();
return <Button title="Go Back" onPress={() => navigation.goBack()} />;
}

React navigation Methods

The navigation object offers core methods like .navigate() for standard screen transitions, alongside specialized methods like .replace() in stack navigators that change screens while maintaining the same navigation history position.

function ExampleScreen({ navigation }: { navigation: any }) {
return (
<View>
{/* Universal methods */}
<Button title="Navigate" onPress={() => navigation.navigate('Profile')} />
<Button title="Go Back" onPress={() => navigation.goBack()} />
{/* Stack-specific methods */}
<Button title="Replace" onPress={() => navigation.replace('Dashboard')} />
<Button title="Push" onPress={() => navigation.push('Profile')} />
<Button title="Pop" onPress={() => navigation.pop()} />
</View>
);
}

The route Prop

Each screen component in React Navigation receives a route prop with name (the screen identifier) and params (passed data), enabling screens to render content based on navigation state.

// Screen component automatically receives route prop
function ProfileScreen({ route }: { route: any }) {
return (
<View>
<Text>Current Screen: {route.name}</Text>
<Text>User ID: {route.params?.userId}</Text>
<Text>Welcome: {route.params?.username || 'Guest'}</Text>
{/* Dynamic rendering based on route data */}
{route.params?.isAdmin && <AdminPanel />}
</View>
);
}
// Navigation with params
function HomeScreen({ navigation }: { navigation: any }) {
return (
<Button
title="Go to Profile"
onPress={() => navigation.navigate('Profile', {
userId: '123',
username: 'John',
isAdmin: true
})}
/>
);
}

Nested Navigators

React Navigation supports nesting navigators to create sophisticated navigation architectures that mirror real-world app structures. Common patterns include embedding tab navigators within stack navigators (allowing users to drill down from tabbed sections into detailed views), or placing multiple independent stack navigators inside drawer navigators (creating separate navigation flows for different app areas). This hierarchical approach enables a scalable organization where each navigator type handles its specific responsibility—stacks manage screen sequences, tabs organize top-level sections, and drawers provide app-wide navigation access.

// Home tab has its own stack
function HomeStack() {
return (
<Stack.Navigator>
<Stack.Screen name="HomeList" component={HomeScreen} />
<Stack.Screen name="HomeDetails" component={DetailsScreen} />
</Stack.Navigator>
);
}
// Main tabs contain the stacks
function MainTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStack} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
}

Expo Router index.tsx

The index.tsx file in Expo Router serves as the default screen for a route directory. When users navigate to a route path, Expo Router automatically renders the corresponding index.tsx file, making it the primary entry point for that route segment.

app/
├── index.tsx → "/" (home route)
└── settings/
├── index.tsx → "/settings" (settings home)
└── profile.tsx → "/settings/profile"
// app/index.tsx - Root route "/"
export default function HomeScreen() {
return (
<View>
<Text>Welcome to the Home Screen</Text>
<Link href="/settings">Go to Settings</Link>
</View>
);
}
// app/settings/index.tsx - Settings route "/settings"
export default function SettingsScreen() {
return (
<View>
<Text>Settings Home</Text>
<Link href="/settings/profile">Edit Profile</Link>
</View>
);
}

Expo Router _layout.tsx File

The _layout.tsx file acts as a navigation wrapper for its directory in Expo Router, controlling how child routes are grouped and navigated between using React Navigation components.

app/
├── _layout.tsx → Root stack navigator
├── index.tsx → Home screen
└── settings/
├── _layout.tsx → Settings tab navigator
├── index.tsx → Settings home
└── profile.tsx → Profile tab
// app/_layout.tsx - Root stack layout
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: 'Home' }} />
<Stack.Screen name="settings" options={{ title: 'Settings' }} />
</Stack>
);
}
// app/settings/_layout.tsx - Tab layout for settings
import { Tabs } from 'expo-router';
export default function SettingsLayout() {
return (
<Tabs>
<Tabs.Screen name="index" options={{ title: 'Settings' }} />
<Tabs.Screen name="profile" options={{ title: 'Profile' }} />
</Tabs>
);
}

Expo Router Dynamic Paths

Dynamic routes in Expo Router use square brackets around folder names (like [id] or [slug]) to create parameterized path segments that capture URL values as route parameters.

app/
├── index.tsx → "/" (home)
├── users/
│ ├── index.tsx → "/users" (user list)
│ └── [id].tsx → "/users/123" (dynamic user profile)
├── posts/
│ ├── index.tsx → "/posts" (post list)
│ ├── [slug].tsx → "/posts/my-blog-post" (dynamic post)
│ └── [slug]/
│ └── comments.tsx → "/posts/my-blog-post/comments"
└── shop/
└── [category]/
├── index.tsx → "/shop/electronics" (category page)
└── [productId].tsx → "/shop/electronics/laptop-123"

useLocalSearchParams() Hook

The useLocalSearchParams() hook extracts dynamic route data in Expo Router, automatically parsing bracket-defined segments like [category]/[productId] into an object of key-value pairs for component use.

app/
├── users/
│ └── [id]/
│ └── index.tsx → "/users/123"
├── shop/
│ └── [category]/
│ └── [productId]/
│ └── index.tsx → "/shop/electronics/laptop-456"
└── posts/
└── [slug]/
└── comments/
└── [commentId]/
└── index.tsx → "/posts/my-blog/comments/789"
// app/users/[id]/index.tsx
export default function UserProfile() {
const { id } = useLocalSearchParams();
// URL: /users/123 → id = "123"
}
// app/shop/[category]/[productId]/index.tsx
export default function ProductPage() {
const { category, productId } = useLocalSearchParams();
// URL: /shop/electronics/laptop-456 → category = "electronics", productId = "laptop-456"
}
// app/posts/[slug]/comments/[commentId]/index.tsx
export default function CommentDetail() {
const { slug, commentId } = useLocalSearchParams();
// URL: /posts/my-blog/comments/789 → slug = "my-blog", commentId = "789"
}

Grouping Routes with Expo Router

Expo Router’s route groups use parentheses notation (groupName) around folder names to create organizational boundaries that remain invisible to the URL structure, enabling logical separation of related screens without affecting user-facing navigation paths.

app/
├── (auth)/
│ ├── login.tsx → "/login"
│ ├── register.tsx → "/register"
│ └── forgot-password.tsx → "/forgot-password"
├── (tabs)/
│ ├── _layout.tsx → Tab navigator
│ ├── index.tsx → "/"
│ ├── profile.tsx → "/profile"
│ └── settings.tsx → "/settings"
├── (shop)/
│ ├── products.tsx → "/products"
│ └── cart.tsx → "/cart"
└── help.tsx → "/help"

Expo Router Redirects

Expo Router’s <Redirect> component programmatically redirects users to different routes when rendered, commonly used for conditional navigation based on authentication state or user permissions.

import { Redirect } from 'expo-router';
// Conditional redirect based on user state
export default function ProtectedScreen() {
const isLoggedIn = false; // Could come from state, storage, etc.
// Redirect unauthenticated users to login
if (!isLoggedIn) {
return <Redirect href="/login" />;
}
return (
<View>
<Text>Welcome to the protected area!</Text>
</View>
);
}
// Simple redirect from old route to new route
export default function OldRoute() {
return <Redirect href="/new-route" />;
}

Passing Dynamic Data

Navigation methods in React Navigation accept a second parameter containing data to pass to the destination screen, where .navigate('ScreenName', { key: value }) sends data accessible through route.params.

Stack Navigator
├── ProductList → navigate('ProductDetails', { productId: '123', name: 'Laptop' })
└── ProductDetails → receives data via route.params
// Sending data when navigating
function ProductListScreen({ navigation }: { navigation: any }) {
return (
<Button
title="View Product"
onPress={() => navigation.navigate('ProductDetails', {
productId: '123',
name: 'Laptop'
})}
/>
);
}
// Receiving data via route.params
function ProductDetailsScreen({ route }: { route: any }) {
const { productId, name } = route.params;
return (
<View>
<Text>Product: {name}</Text>
<Text>ID: {productId}</Text>
</View>
);
}

Learn more on Codecademy

  • Build mobile apps with TypeScript and React, using Expo and React Native
    • Intermediate.
      12 hours