File-based router for React Native and web applications built on React Navigation. Automatic deep linking, universal navigation across platforms.
- All routes live in
app/directory - Each file = a screen/page with automatic URL
app/index.tsx= initial route (/)app/_layout.tsx= root layout (replaces App.tsx)- Non-navigation code goes outside
app/
home.tsx→/home(static route)[id].tsx→/123(dynamic route)(tabs)/→ route group (doesn't affect URL)index.tsx→ default route for directory_layout.tsx→ navigation layout+not-found.tsx→ catch-all error page
import { useRouter } from 'expo-router';
const router = useRouter();
router.navigate('/about'); // Navigate to route
router.push('/about'); // Push onto stack
router.back(); // Go back
router.replace('/about'); // Replace currentimport { Link } from 'expo-router';
<Link href="/about">About</Link>
<Link href="/user/123">User Profile</Link>
// With params
<Link href={{
pathname: '/user/[id]',
params: { id: '123' }
}}>Profile</Link>
// Pressable links
<Link href="/about" asChild>
<Pressable><Text>About</Text></Pressable>
</Link>// File: app/user/[id].tsx
import { useLocalSearchParams } from 'expo-router';
export default function User() {
const { id } = useLocalSearchParams();
return <Text>User ID: {id}</Text>;
}import { Stack } from 'expo-router';
import { useFonts } from 'expo-font';
export default function RootLayout() {
const [loaded] = useFonts({ /* fonts */ });
if (!loaded) return null;
return <Stack />;
}import { Stack } from 'expo-router';
export default function Layout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: 'Home' }} />
<Stack.Screen name="about" options={{ headerShown: false }} />
</Stack>
);
}import { Tabs } from 'expo-router';
import { FontAwesome } from '@expo/vector-icons';
export default function TabLayout() {
return (
<Tabs screenOptions={{ tabBarActiveTintColor: 'blue' }}>
<Tabs.Screen
name="index"
options={{
title: 'Home',
tabBarIcon: ({ color }) =>
<FontAwesome size={28} name="home" color={color} />
}}
/>
<Tabs.Screen name="settings" options={{ title: 'Settings' }} />
</Tabs>
);
}import { Slot } from 'expo-router';
export default function Layout() {
return (
<>
<Header />
<Slot />
<Footer />
</>
);
}app/
_layout.tsx # Root stack
(tabs)/
_layout.tsx # Tab navigator
index.tsx # Home tab
profile.tsx # Profile tab
modal.tsx # Modal over tabs
// app/_layout.tsx
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="login" options={{ presentation: 'modal' }} />
</Stack>
);
}import { Redirect } from 'expo-router';
export default function ProtectedPage() {
const { user } = useAuth();
if (!user) {
return <Redirect href="/login" />;
}
return <Text>Protected Content</Text>;
}- Use
router.navigate()for most navigation - Relative paths:
./page(current dir),../page(parent dir) - Query params:
href="/users?limit=20"orrouter.setParams({ limit: 20 }) - Deep links work automatically:
myapp://profile/123 - Set
initialRouteNamein layouts for proper back navigation - Use
<Link prefetch />for faster navigation - Hide tabs:
<Tabs.Screen options={{ href: null }} />
app/
_layout.tsx # Root layout
index.tsx # Home page (/)
about.tsx # About page (/about)
(tabs)/
_layout.tsx # Tab layout
index.tsx # Home tab (/)
profile.tsx # Profile tab (/profile)
user/
[id].tsx # Dynamic user page (/user/123)
+not-found.tsx # 404 page
Ready to build! 🚀