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: 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.Navigatorto define the drawer structure - Each screen in the drawer is defined using
Drawer.Screen - Customize appearance with
screenOptionsanddrawerLabel - 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'); |
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.