Skip to main content
· 3 min readExpoTutorials
Malik Chohra

By Malik Chohra

Use React Navigation in Expo (2026 Guide)

Comprehensive guide to React Navigation with Stack, Tab, and Drawer navigators. Includes TypeScript, deep linking, custom headers, and performance optimization.

Mastering React Navigation in Expo

React Navigation is the de facto standard for routing and navigation in React Native applications. With Expo, setting up and configuring navigation becomes even more streamlined. This comprehensive guide covers everything from basic setup to advanced patterns including TypeScript integration, deep linking, and performance optimization.

Whether you're building a simple app with basic navigation or a complex application with nested navigators and deep linking, this guide provides the patterns and best practices you need.

A quick note on terminology: modern Expo projects use Expo Router, a file-based layer built directly on top of React Navigation. You get the same Stack, Tabs, and Drawer navigators, but routes are defined by your folder structure inside app/ rather than wired up by hand. The examples below use Expo Router, with the underlying React Navigation config shown where it matters.

Getting Started

Install React Navigation in your Expo project:

npx expo install @react-navigation/native
npx expo install react-native-screens react-native-safe-area-context
npx expo install @react-navigation/native-stack
npx expo install @react-navigation/bottom-tabs
npx expo install @react-navigation/drawer

Stack Navigator

The Stack Navigator provides a way to transition between screens where each new screen is placed on top of a stack:

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

export default function RootLayout() {
  return (
    <Stack>
      <Stack.Screen name="index" options={{ title: 'Home' }} />
      <Stack.Screen name="details" options={{ title: 'Details' }} />
    </Stack>
  );
}

Tab Navigator

Bottom tab navigation is perfect for primary navigation in mobile apps:

// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';

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

Drawer Navigator

Drawer navigation provides a hidden menu that slides in from the side, common for apps with many top-level destinations. With Expo Router you declare it the same way as Tabs:

// app/(drawer)/_layout.tsx
import { Drawer } from 'expo-router/drawer';

export default function DrawerLayout() {
  return (
    <Drawer screenOptions={{ drawerType: 'slide' }}>
      <Drawer.Screen name="index" options={{ title: 'Home' }} />
      <Drawer.Screen name="settings" options={{ title: 'Settings' }} />
    </Drawer>
  );
}
  • Gestures - Users swipe from the edge to open the drawer; it works out of the box with react-native-gesture-handler.
  • Custom content - Pass a drawerContent prop to render your own header, avatar, and menu items.

Nesting Navigators

Real apps almost always nest navigators, a tab bar where one tab contains its own stack, for example. With file-based routing this is simply nested folders: a (tabs) group whose feed tab is a folder containing its own _layout.tsx Stack. Keep nesting shallow where you can, every extra layer makes params and back-behavior harder to reason about.

TypeScript Integration

Type-safe navigation with TypeScript prevents common navigation errors:

// types/navigation.ts
export type RootStackParamList = {
  Home: undefined;
  Details: { id: string };
  Profile: { userId: string };
};

// Usage with type safety
navigation.navigate('Details', { id: '123' }); // ✅ Type safe
navigation.navigate('Details'); // ❌ TypeScript error - missing params

Deep Linking

Enable deep linking to allow users to navigate directly to specific screens via URLs:

// app.json
{
  "expo": {
    "scheme": "myapp",
    "android": {
      "intentFilters": [{
        "action": "VIEW",
        "data": [{ "scheme": "myapp" }]
      }]
    }
  }
}

Custom Headers

Customize navigation headers for branding and functionality:

  • Custom header components
  • Header buttons and actions
  • Dynamic header titles
  • Header styling and theming

Lazy Loading

Improve app startup performance with lazy-loaded screens:

const DetailsScreen = lazy(() => import('./screens/Details'));

<Stack.Screen
  name="details"
  component={DetailsScreen}
  options={{ lazy: true }}
/>

Performance Optimization

Optimize navigation performance for smooth user experience:

  • Enable screen optimization flags
  • Use lazy loading for heavy screens
  • Optimize animations and transitions
  • Implement proper state management

Get Navigation Done Right

For Developers: AI Mobile Launcher includes pre-configured navigation with tabs, stacks, deep linking, and TypeScript support, ready for production.

For Founders: Need a mobile app with professional navigation? Contact AI Mobile Launcher for expert development.

Related reading