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 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 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 sectionsconst Tab = createBottomTabNavigator();<Tab.Navigator><Tab.Screen name="Home" component={HomeScreen} /><Tab.Screen name="Profile" component={ProfileScreen} /></Tab.Navigator>// Drawer: Sidebar menuconst Drawer = createDrawerNavigator();<Drawer.Navigator><Drawer.Screen name="Main" component={MainTabs} /><Drawer.Screen name="Settings" component={SettingsScreen} /></Drawer.Navigator>
<NavigationContainer> in NavigationThe <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 navigationfunction HomeScreen({ navigation }) {return (<Buttontitle="Go to Profile"onPress={() => navigation.navigate('Profile')}/>);}
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 packageimport { createNativeStackNavigator } from '@react-navigation/native-stack';// 2. Call the creation functionconst 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>);}
create[Type]Navigator MethodThe 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.NavigatorinitialRouteName="Home"screenOptions={{headerStyle: { backgroundColor: '#f4511e' },animation: 'slide_from_right'}}>{/* Screen: Defines individual routes with names and components */}<Stack.Screenname="Home"component={HomeScreen}options={{ title: 'Welcome Home' }}/><Stack.Screenname="Profile"component={ProfileScreen}options={{title: 'User Profile',headerRight: () => <EditButton />}}/></Stack.Navigator>);}
navigation ObjectThe 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 (<Buttontitle="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()} />;}
navigation MethodsThe 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>);}
route PropEach 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 propfunction 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 paramsfunction HomeScreen({ navigation }: { navigation: any }) {return (<Buttontitle="Go to Profile"onPress={() => navigation.navigate('Profile', {userId: '123',username: 'John',isAdmin: true})}/>);}
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 stackfunction HomeStack() {return (<Stack.Navigator><Stack.Screen name="HomeList" component={HomeScreen} /><Stack.Screen name="HomeDetails" component={DetailsScreen} /></Stack.Navigator>);}// Main tabs contain the stacksfunction MainTabs() {return (<Tab.Navigator><Tab.Screen name="Home" component={HomeStack} /><Tab.Screen name="Profile" component={ProfileScreen} /></Tab.Navigator>);}
index.tsxThe 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>);}
_layout.tsx FileThe _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 layoutimport { 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 settingsimport { 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>);}
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() HookThe 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.tsxexport default function UserProfile() {const { id } = useLocalSearchParams();// URL: /users/123 → id = "123"}// app/shop/[category]/[productId]/index.tsxexport default function ProductPage() {const { category, productId } = useLocalSearchParams();// URL: /shop/electronics/laptop-456 → category = "electronics", productId = "laptop-456"}// app/posts/[slug]/comments/[commentId]/index.tsxexport default function CommentDetail() {const { slug, commentId } = useLocalSearchParams();// URL: /posts/my-blog/comments/789 → slug = "my-blog", commentId = "789"}
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’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 stateexport default function ProtectedScreen() {const isLoggedIn = false; // Could come from state, storage, etc.// Redirect unauthenticated users to loginif (!isLoggedIn) {return <Redirect href="/login" />;}return (<View><Text>Welcome to the protected area!</Text></View>);}// Simple redirect from old route to new routeexport default function OldRoute() {return <Redirect href="/new-route" />;}
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 navigatingfunction ProductListScreen({ navigation }: { navigation: any }) {return (<Buttontitle="View Product"onPress={() => navigation.navigate('ProductDetails', {productId: '123',name: 'Laptop'})}/>);}// Receiving data via route.paramsfunction ProductDetailsScreen({ route }: { route: any }) {const { productId, name } = route.params;return (<View><Text>Product: {name}</Text><Text>ID: {productId}</Text></View>);}