Expo Router 3 — nawigacja w React Native bez bólu

React Navigation był przez lata jedynym sensownym wyborem do nawigacji w React Native. Działa, jest elastyczny, ma ogromną społeczność. Ma też jeden fundamentalny problem: każdą trasę musisz zdefiniować ręcznie w konfiguracji. Przy 20+ ekranach to jest boilerplate, który boli.

Expo Router 3 rozwiązuje ten problem radykalnie — plikami. Tworzysz plik `app/profile/[id].tsx`, automatycznie masz trasę `/profile/:id`. Znajome jeśli pracowałeś z Next.js. I to jest właśnie punkt — ta sama mentalność, ten sam Developer Experience, tylko na mobile.

Expo Router 3 vs React Navigation: czym się różnią

React Navigation to **konfiguracyjny** model nawigacji. Definiujesz navigator, rejestrujesz w nim screeny, przekazujesz props. Masz pełną kontrolę, ale za cenę ceremonii.

```typescript // React Navigation — klasyczna konfiguracja const Stack = createStackNavigator()

function AppNavigator() { return ( ) } ```

Expo Router to **file-based** model. Struktura katalogów = struktura nawigacji. Zero konfiguracji dla podstawowych przypadków.

``` app/ index.tsx → / profile/ [id].tsx → /profile/:id (tabs)/ _layout.tsx → definicja tab baru home.tsx → /home (tab) settings.tsx → /settings (tab) ```

Kluczowe różnice: - **Expo Router** — mniej boilerplate, URL-based nawigacja, wbudowany deep linking, lepsza integracja z web (Expo Web) - **React Navigation** — więcej kontroli, dojrzalszy ekosystem, łatwiej w legacy projektach, więcej opcji animacji

Setup: nowy projekt z TypeScript

```bash npx create-expo-app@latest my-app --template # Wybierz: "Navigation (TypeScript)" cd my-app npx expo start ```

Template tworzy gotową strukturę z Expo Router, TypeScript i podstawowym layoutem tab-based. Nic więcej nie potrzebujesz żeby zacząć.

Podstawowa struktura plików

``` app/ _layout.tsx ← root layout (provider'y, fonts) index.tsx ← ekran główny / (tabs)/ _layout.tsx ← konfiguracja tab navigatora index.tsx ← pierwsza zakładka explore.tsx ← druga zakładka profile/ [id].tsx ← dynamiczny segment +not-found.tsx ← 404 ```

**Root layout** (`app/_layout.tsx`):

```typescript import { Stack } from 'expo-router' import { useFonts } from 'expo-font' import * as SplashScreen from 'expo-splash-screen' import { useEffect } from 'react'

SplashScreen.preventAutoHideAsync()

export default function RootLayout() { const [loaded] = useFonts({ SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), })

useEffect(() => { if (loaded) SplashScreen.hideAsync() }, [loaded])

if (!loaded) return null

return ( ) } ```

Tab layout z ikonami

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

export default function TabLayout() { return ( ( ), }} /> ( ), }} /> ) } ```

Dynamiczne segmenty

```typescript // app/profile/[id].tsx import { useLocalSearchParams, useRouter } from 'expo-router' import { View, Text, Button, StyleSheet } from 'react-native'

export default function ProfileScreen() { const { id } = useLocalSearchParams<{ id: string }>() const router = useRouter()

return ( Profil użytkownika: {id}