Skip to main content

Working with Expo Router

1. Creating Routes

Route Path Type Description Example
/home Static Route Simple static route for a page /home
/user/[id] Dynamic Route Matches any id value /user/123
/blog/[...slug] Wildcard Route Captures entire remaining path /blog/2023/12/01

Example: Basic Route Setup

// File: app/home.js
export default function Home() {
  return <View><Text>Welcome to Home</Text></View>;
}

Example: Dynamic Route

// File: app/user/[id].js
import { useParams } from 'expo-router';

export default function User() {
  const { id } = useParams();
  return <View><Text>User ID: {id}</Text></View>;
}
Note: Route files must be placed inside the app directory. Nested folders create nested routes.

2. Creating Layout Files

Layout files in Expo Router define the structure and appearance of your app's navigation and UI. They are React components that wrap your routes and can include shared headers, footers, or navigation elements.

File Name Description Usage
_layout.tsx Root layout file for the app Defines global structure and navigation
_layout.tsx Layout file for a specific route group Applies to all routes within that group

Example: Root Layout File

<View style={{ flex: 1 }}>
  <Text>Root Layout</Text>
  <Stack />
</View>

Example: Route Group Layout

<View style={{ flex: 1, padding: 20 }}>
  <Text>Dashboard Layout</Text>
  <Slot />
</View>
Note: Layout files must export a React component and can use Slot to render child routes.
Warning: Layout files are not included in the final bundle if they are not used.

3. Navigating Between Screens

Method Description Usage
router.push() Navigate to a new screen router.push('/profile')
router.replace() Replace current screen in history router.replace('/home')
router.pop() Go back to previous screen router.pop()
router.back() Go back one screen (alias for pop) router.back()

Example: Using router.push()

import { useRouter } from 'expo-router';

export default function HomeScreen() {
  const router = useRouter();

  const handlePress = () => {
    router.push('/profile');
  };

  return (
    <Button title="Go to Profile" onPress={handlePress} />
  );
}

Example: Using router.replace()

import { useRouter } from 'expo-router';

export default function LoginScreen() {
  const router = useRouter();

  const handleLogin = () => {
    router.replace('/dashboard');
  };

  return (
    <Button title="Login" onPress={handleLogin} />
  );
}
Note: router.push() adds to the navigation stack, while router.replace() replaces the current screen.

4. Using Dynamic Routes

Dynamic routes in Expo Router allow you to create flexible navigation paths that can handle variable segments in the URL. These routes are defined using square brackets [] in the file path.

Route Pattern Description Example URL Matched Param
[id] Single dynamic segment /user/123 id = 123
[user]/[post] Multiple dynamic segments /john/123 user = john, post = 123
[...rest] Wildcard route (captures remaining path) /blog/2023/12/01 rest = blog/2023/12/01

Example: Accessing Dynamic Route Parameters

// File: app/[id]/index.js
import { useLocalSearchParams } from 'expo-router';

export default function UserPage() {
  const { id } = useLocalSearchParams();
  
  return (
    <div>
      <p>User ID: {id}</p>
    </div>
  );
}

Example: Nested Dynamic Routes

// File: app/users/[userId]/posts/[postId]/index.js
import { useLocalSearchParams } from 'expo-router';

export default function PostPage() {
  const { userId, postId } = useLocalSearchParams();
  
  return (
    <div>
      <p>User ID: {userId}</p>
      <p>Post ID: {postId}</p>
    </div>
  );
}
Note: Dynamic route segments are matched in order, and the [...rest] wildcard must be at the end of a route path.

5. Creating Tabs Layout

Example: Basic Tab Layout

// app/(tabs)/_layout.js
import { Tabs } from 'expo-router';

export default function TabLayout() {
  return (
    <Tabs>
      <Tabs.Screen name="index" options={{ title: 'Home' }} />
      <Tabs.Screen name="profile" options={{ title: 'Profile' }} />
      <Tabs.Screen name="settings" options={{ title: 'Settings' }} />
    </Tabs>
  );
}
Property Description Default
name Screen route name (must match file path) Required
options Tab configuration (title, icon, etc.) Optional
Note: Tab layouts must be defined in a _layout.js file inside a group directory (e.g., app/(tabs)/_layout.js)

Example: Tab with Icons

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

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

6. Creating Stack Layout

Example: Basic Stack Layout

// app/+layout.js
import { Stack } from 'expo-router';

export default function Layout() {
  return <Stack screenOptions={{ headerShown: false }} />;
}
Property Description Default
screenOptions Global options for all screens in the stack {}
initialRouteName Name of the screen to start with First screen in stack
keyboardHandling Controls keyboard handling behavior true
Note: Stack layout is used to manage navigation between screens in a stack-based navigation structure.
Warning: Ensure that all screen components are properly registered in the Expo Router configuration.

7. Creating Drawer Layout

Example: Basic Drawer Navigation Setup

import { Drawer } from 'expo-router';
import { View, Text } from 'react-native';

export default function DrawerLayout() {
  return (
    <Drawer.Screen
      name="drawer"
      options={{
        title: 'Navigation Drawer',
        drawerLabel: 'Home'
      }}
    >
      <Drawer.Navigator
        initialRouteName="home"
        screenOptions={{
          headerShown: true,
          headerTitle: 'My App'
        }}
      >
        <Drawer.Screen
          name="home"
          component={HomeScreen}
          options={{ title: 'Home' }}
        />
        <Drawer.Screen
          name="profile"
          component={ProfileScreen}
          options={{ title: 'Profile' }}
        />
      </Drawer.Navigator>
    </Drawer.Screen>
  );
}
Property Description Default
initialRouteName Route to start the drawer from "home"
screenOptions Global options for all screens in the drawer {}
drawerLabel Text shown in the drawer for this screen "Home"
Note: The Drawer.Navigator must be wrapped inside a Drawer.Screen to work with Expo Router.
Warning: Drawer navigation is not supported in all platforms. Ensure compatibility with your target platforms.

Drawer Navigation Summary

  • Use Drawer.Navigator to define the drawer structure
  • Each screen in the drawer is defined using Drawer.Screen
  • Customize appearance with screenOptions and drawerLabel
  • Supports nested navigation stacks within drawer items

8. Handling Route Parameters

Parameter Type Description
params Record<string, string> Object containing route parameter values

Example: Accessing Route Parameters

import { useRouter } from 'expo-router';

export function UserProfile() {
  const router = useRouter();
  const { id } = router.params;

  return (
    <View>
      <Text>User ID: {id}</Text>
    </View>
  );
}

Example: Using Parameters in Navigation

import { router } from 'expo-router';

function NavigateToUser() {
  const navigate = () => {
    router.push(`/user/123`);
  };

  return <Button title="Go to User" onPress={navigate} />;
}
Note: Route parameters are automatically decoded and made available in the params object.

9. Implementing Redirects

Example: Redirect from a Route

import { Redirect } from 'expo-router';

export default function RedirectToHome() {
  return <Redirect href="/" />;
}
Property Values Description
href string The path to redirect to
as string Optional component to render as (e.g., 'Link')
Note: Redirects are handled on the client-side and do not trigger a full page reload.

Example: Redirect with Query Parameters

import { Redirect } from 'expo-router';

export default function RedirectToProfile() {
  return <Redirect href="/profile?id=123" />;
}

10. Creating Not Found Screen

Property Description Example
404.tsx File name for the not found screen in Expo Router 404.tsx
useRouter() Hook to access router instance const router = useRouter();

Example: Basic 404 Page

<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
  <Text>Page not found</Text>
</View>

Example: Redirect to Home

import { useRouter } from 'expo-router';

export default function NotFoundScreen() {
  const router = useRouter();

  useEffect(() => {
    router.replace('/');
  }, []);

  return <View style={{ flex: 1 }} />;
}
Note: The 404.tsx file must be placed in the root of your Expo project's app directory to be recognized as a not found screen.

11. Using Typed Routes

Property Description Example
router Router instance for navigation const router = useRouter();
href Route path or object with params router.push('/user/123');
as Optional route alias for navigation router.push({ pathname: '/user/[id]', params: { id: '123' } });

Example: Typed Route Navigation

import { useRouter } from 'expo-router';

export default function UserProfile() {
  const router = useRouter();

  const handleNavigate = () => {
    // Typed route navigation
    router.push({
      pathname: '/user/[id]',
      params: { id: '123' }
    });
  };

  return (
    <button onPress={handleNavigate}>Go to User</button>
  );
}
Note: Typed routes require expo-router v3+ and must be configured in app.config.ts with expo-router plugin.
Warning: Route parameters must match the defined route pattern exactly to avoid runtime errors.

12. Implementing Deep Linking

Property Description Example
useLinkTo Hook to navigate to a specific route with deep linking support const linkTo = useLinkTo('/profile/123');
Link Component to create deep links in the UI <Link href="/home" />
router.push Method to navigate with deep links router.push('/user/123');
<Link href="/user/123">
  Go to User Profile
</Link>

Example: Using useLinkTo Hook

import { useLinkTo } from 'expo-router';

function MyComponent() {
  const linkTo = useLinkTo();
  
  return (
    <Button
      title="Go to Profile"
      onPress={() => linkTo('/profile/123')}
    />
  );
}
Note: Deep links in Expo Router are handled automatically by the router and can be used to navigate between screens or open external URLs.

Was this article helpful?