Expo Router w React Native: Kompletny przewodnik po nawigacji opartej na plikach
Kompletny przewodnik po Expo Router w React Native — nawigacja oparta na plikach, layouty, trasy dynamiczne, typowana nawigacja oraz zaawansowane wzorce jak modale i zakładki.

Expo Router wprowadza do React Native nawigację opartą na strukturze plików, zastępując ręczną konfigurację nawigacji podejściem konwencyjnym inspirowanym Next.js. Począwszy od Expo SDK 57 i Expo Router, budowanie wieloplatformowej nawigacji dla Androida, iOS-a i aplikacji webowych sprowadza się do tworzenia plików w odpowiednim katalogu.
Nowe projekty Expo są dostarczane z wstępnie skonfigurowanym Expo Routerem. Wystarczy uruchomić npx create-expo-app@latest --template default, aby rozpocząć pracę z nawigacją opartą na plikach. W istniejących projektach wystarczy zainstalować expo-router i zaktualizować punkt wejścia aplikacji.
Jak działa nawigacja oparta na plikach w Expo Router
Każdy plik w katalogu app automatycznie staje się trasą. Ścieżka pliku mapuje się bezpośrednio na ścieżkę URL, eliminując potrzebę centralnej konfiguracji nawigacji. Plik app/settings.tsx tworzy trasę /settings, a app/profile/edit.tsx mapuje się na /profile/edit.
Takie podejście oferuje trzy kluczowe zalety w porównaniu z tradycyjną konfiguracją React Navigation:
- Zerowa konfiguracja: trasy istnieją w momencie utworzenia pliku
- Automatyczne deep linking: każdy ekran otrzymuje URL, co umożliwia udostępnianie i testowanie
- Typowana nawigacja: TypeScript wie, które trasy istnieją w czasie kompilacji
import { View, Text, StyleSheet } from 'react-native'
import { Link } from 'expo-router'
export default function HomeScreen() {
return (
<View style={styles.container}>
<Text style={styles.title}>Welcome</Text>
{/* Link maps directly to file path */}
<Link href="/settings" style={styles.link}>
Open Settings
</Link>
<Link href="/profile/edit" style={styles.link}>
Edit Profile
</Link>
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', padding: 24 },
title: { fontSize: 28, fontWeight: 'bold', marginBottom: 16 },
link: { fontSize: 16, color: '#61DAFB', marginTop: 12 },
})Komponent Link obsługuje nawigację na wszystkich platformach. W przeglądarce renderuje tag <a> z odpowiednim atrybutem href dla SEO. Na platformach natywnych uruchamia nawigację opartą na stosie.
Struktura projektu i pliki layoutu
Expo Router wykorzystuje pliki _layout.tsx do definiowania kontenerów nawigacji. Każdy katalog może mieć własny layout, tworząc zagnieżdżone hierarchie nawigacji. Layout główny opakowuje całą aplikację, natomiast zagnieżdżone layouty kontrolują poszczególne sekcje.
Typowa struktura projektu wygląda następująco:
app/
_layout.tsx # Root layout (Stack or custom)
index.tsx # Home screen (/)
(tabs)/ # Tab group (parentheses = route group)
_layout.tsx # Tab navigator
home.tsx # /home tab
search.tsx # /search tab
profile.tsx # /profile tab
settings/
_layout.tsx # Settings stack layout
index.tsx # /settings
notifications.tsx # /settings/notifications
privacy.tsx # /settings/privacyGrupy tras — katalogi ujęte w nawiasy — organizują pliki bez wpływu na URL. Katalog (tabs) powyżej tworzy nawigator zakładek, ale adresy URL pozostają w postaci /home, /search i /profile, a nie /tabs/home.
import { Stack } from 'expo-router'
export default function RootLayout() {
return (
<Stack
screenOptions={{
headerStyle: { backgroundColor: '#1a1a2e' },
headerTintColor: '#ffffff',
headerTitleStyle: { fontWeight: '600' },
}}
>
<Stack.Screen name="index" options={{ title: 'Home' }} />
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="settings" options={{ title: 'Settings' }} />
</Stack>
)
}Główny layout służy również jako miejsce do ładowania czcionek, inicjalizacji providerów i konfiguracji globalnych ustawień.
Budowanie nawigacji zakładkowej z Expo Router
Nawigacja zakładkowa wymaga pliku _layout.tsx wewnątrz grupy tras. Expo Router udostępnia zarówno NativeTabs dla doświadczeń zakładkowych specyficznych dla platformy, jak i standardowy komponent Tabs dla większości przypadków użycia. Od SDK 56 Native Tabs obsługuje właściwość disabled umożliwiającą zablokowanie wyboru zakładki przy jednoczesnym zachowaniu jej widoczności.
import { Tabs } from 'expo-router'
import { Ionicons } from '@expo/vector-icons'
export default function TabLayout() {
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: '#61DAFB',
tabBarInactiveTintColor: '#888',
tabBarStyle: {
backgroundColor: '#1a1a2e',
borderTopColor: '#2d2d44',
},
}}
>
<Tabs.Screen
name="home"
options={{
title: 'Home',
tabBarIcon: ({ color, size }) => (
<Ionicons name="home" size={size} color={color} />
),
}}
/>
<Tabs.Screen
name="search"
options={{
title: 'Search',
tabBarIcon: ({ color, size }) => (
<Ionicons name="search" size={size} color={color} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
title: 'Profile',
tabBarIcon: ({ color, size }) => (
<Ionicons name="person" size={size} color={color} />
),
}}
/>
</Tabs>
)
}Każdy plik ekranu zakładki eksportuje standardowy komponent React. Ikona, etykieta i znacznik paska zakładek konfigurowane są za pomocą właściwości options w layoucie.
Trasy dynamiczne i parametry tras
Dynamiczne segmenty wykorzystują nawiasy kwadratowe w nazwie pliku. Plik o nazwie [id].tsx dopasowuje dowolny pojedynczy segment, natomiast [...slug].tsx przechwytuje wszystkie pozostałe segmenty.
import { View, Text, StyleSheet } from 'react-native'
import { useLocalSearchParams, Stack } from 'expo-router'
export default function ProductScreen() {
// Extract the dynamic parameter from the URL
const { id } = useLocalSearchParams<{ id: string }>()
return (
<View style={styles.container}>
<Stack.Screen options={{ title: \`Product \${id}\` }} />
<Text style={styles.heading}>Product Details</Text>
<Text style={styles.id}>ID: {id}</Text>
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 24 },
heading: { fontSize: 24, fontWeight: 'bold', marginBottom: 8 },
id: { fontSize: 16, color: '#888' },
})Przejście do /product/42 renderuje ten ekran z parametrem id ustawionym na "42". Hook useLocalSearchParams zapewnia typowany dostęp do wszystkich parametrów trasy.
Dla tras typu catch-all, [...slug].tsx przechwytuje całe segmenty ścieżki:
import { useLocalSearchParams } from 'expo-router'
export default function DocsScreen() {
// /docs/getting-started/installation → slug = ['getting-started', 'installation']
const { slug } = useLocalSearchParams<{ slug: string[] }>()
return <DocViewer path={slug.join('/')} />
}Gotowy na rozmowy o React Native?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Typowane trasy dla bezpieczeństwa w czasie kompilacji
Expo Router automatycznie generuje typy tras po włączeniu opcji typed routes. Pozwala to wychwytywać nieprawidłowe linki w czasie kompilacji, a nie w czasie wykonywania.
Aktywacja typowanych tras w pliku app.json:
{
"expo": {
"experiments": {
"typedRoutes": true
}
}
}Po włączeniu tej opcji, właściwość href komponentu Link oraz argument funkcji router.push() akceptują wyłącznie prawidłowe ciągi znaków tras:
import { router } from 'expo-router'
function handleCheckout(cartId: string) {
// TypeScript validates this route exists
router.push(\`/product/\${cartId}\`)
// This would cause a compile error if /nonexistent doesn't exist
// router.push('/nonexistent')
}Typowane trasy doskonale współpracują z useLocalSearchParams. Wygenerowane typy zapewniają, że nazwy parametrów są spójne między definicją trasy a komponentem ją konsumującym, zapobiegając subtelnym błędom ujawniającym się dopiero podczas nawigacji do konkretnego ekranu.
Ekrany modalne i opcje prezentacji
Modale w Expo Router to zwykłe ekrany skonfigurowane z opcją presentation: 'modal' w layoucie. Takie podejście zachowuje konwencję opartą na plikach — modal to po prostu kolejna trasa.
import { Stack } from 'expo-router'
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="index" />
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
{/* Modal screen slides up from the bottom */}
<Stack.Screen
name="create-post"
options={{
presentation: 'modal',
headerTitle: 'New Post',
}}
/>
</Stack>
)
}import { View, TextInput, Button, StyleSheet } from 'react-native'
import { router } from 'expo-router'
import { useState } from 'react'
export default function CreatePostModal() {
const [title, setTitle] = useState('')
const handleSubmit = () => {
// Submit logic here
router.back() // Dismiss the modal
}
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Post title"
value={title}
onChangeText={setTitle}
/>
<Button title="Publish" onPress={handleSubmit} />
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 24 },
input: {
borderWidth: 1,
borderColor: '#333',
borderRadius: 8,
padding: 12,
fontSize: 16,
marginBottom: 16,
},
})Przejście do /create-post uruchamia prezentację modalną. Wywołanie router.back() zamyka modal, wracając do poprzedniego ekranu w stosie.
Programowa nawigacja i API routera
Oprócz komponentu Link, Expo Router udostępnia imperatywne API przez obiekt router. Obsługuje ono nawigację wywoływaną logiką biznesową, a nie bezpośrednimi interakcjami użytkownika.
import { router } from 'expo-router'
// Push a new screen onto the stack
router.push('/profile/settings')
// Replace the current screen (no back button)
router.replace('/login')
// Go back to the previous screen
router.back()
// Navigate with parameters
router.push({
pathname: '/product/[id]',
params: { id: '42', source: 'recommendations' },
})
// Check if going back is possible
import { useRouter } from 'expo-router'
function BackButton() {
const router = useRouter()
return router.canGoBack() ? (
<Button title="Back" onPress={() => router.back()} />
) : null
}Różnica między router.push a router.replace jest istotna przy przepływach uwierzytelniania w React Native. Po pomyślnym logowaniu router.replace('/dashboard') uniemożliwia powrót do ekranu logowania.
router.replace() zastępuje bieżący wpis w historii nawigacji. Dla strażników uwierzytelniania, które przekierowują niezalogowanych użytkowników, należy użyć <Redirect href="/login" /> wewnątrz renderowania komponentu — uruchamia się ono w fazie renderowania i działa poprawnie z renderowaniem po stronie serwera.
Middleware i ochrona tras
Expo Router udostępnia middleware serwerowe dla logiki na poziomie tras. Plik +middleware.ts przechwytuje żądania przed dotarciem do komponentu trasy, umożliwiając sprawdzanie uwierzytelniania, przekierowania i manipulację nagłówkami.
import { type MiddlewareRequest } from 'expo-router/server'
export function middleware(request: MiddlewareRequest) {
const { pathname } = request.nextUrl
// Protect dashboard routes
const protectedPaths = ['/dashboard', '/settings', '/profile']
const isProtected = protectedPaths.some(p => pathname.startsWith(p))
if (isProtected) {
const token = request.cookies.get('session')
if (!token) {
return Response.redirect(new URL('/login', request.url))
}
}
return undefined // Continue to route
}Na platformach natywnych, gdzie middleware serwerowe nie działa, ochrona tras opiera się na zabezpieczeniach po stronie klienta. Powszechnym wzorcem jest opakowywanie chronionych layoutów sprawdzaniem uwierzytelniania:
import { Redirect, Stack } from 'expo-router'
import { useAuth } from '@/hooks/useAuth'
export default function AuthenticatedLayout() {
const { isLoggedIn, isLoading } = useAuth()
if (isLoading) return null
if (!isLoggedIn) return <Redirect href="/login" />
return <Stack />
}Middleware serwerowe działa wyłącznie w sieci web z renderowaniem po stronie serwera. W aplikacjach natywnych należy zawsze implementować zabezpieczenia tras po stronie klienta w komponentach layoutu. Połączenie obu podejść gwarantuje spójną ochronę na wszystkich platformach.
Migracja SDK 56: Fork React Navigation
Począwszy od Expo SDK 56, Expo Router nie zależy już od pakietów @react-navigation/*. Jeśli projekt importuje bezpośrednio z React Navigation, należy zaktualizować importy:
| Oryginalny pakiet | Nowa lokalizacja |
|---|---|
@react-navigation/native | expo-router/react-navigation |
@react-navigation/stack | expo-router/js-stack |
@react-navigation/bottom-tabs | expo-router/js-tabs |
Codemod automatycznie aktualizuje importy:
npx expo-codemod sdk-56-expo-router-react-navigation-replace srcAPI w czasie wykonywania pozostaje niezmienione. Zmienione zostały wyłącznie specyfikatory modułów. Biblioteki zewnętrzne nadal importujące z @react-navigation/core działają automatycznie, ponieważ Expo CLI przepisuje te importy podczas bundlowania.
Źródła
- Dokumentacja Expo Router — oficjalny przewodnik i dokumentacja API
- Expo SDK 57 Changelog — aktualne informacje o wydaniu stabilnym
- Expo Router v56 Blog Post — wyjaśnienie forka React Navigation
- Przewodnik migracji SDK 55 do 56 — kroki migracji importów
Czego rekruterzy oczekują w temacie Expo Router
- Expo Router mapuje każdy plik w katalogu
appna trasę automatycznie, eliminując ręczną konfigurację nawigacji - Layouty definiowane przez pliki
_layout.tsxtworzą hierarchie nawigacji dla stosów, zakładek i szuflad - Trasy dynamiczne z
[param].tsxi trasy catch-all z[...slug].tsxobsługują nawigację z parametrami z pełnym wsparciem TypeScript - Typowane trasy wykrywają nieprawidłowe linki nawigacyjne w czasie kompilacji po włączeniu w
app.json - Ekrany modalne, programowa nawigacja przez
router.push/replace/backoraz middleware serwerowe uzupełniają zestaw narzędzi nawigacyjnych - Grupy tras z katalogami w nawiasach organizują kod bez wpływu na adresy URL
- Zabezpieczenia tras po stronie klienta w layoutach obsługują uwierzytelnianie na platformach natywnych, a middleware serwerowe pokrywa sieć web
- Od SDK 56 importy z
@react-navigation/*muszą być zaktualizowane do odpowiednikówexpo-router/*
Więcej informacji o architekturze React Native i przygotowaniu do rozmów kwalifikacyjnych znajduje się w module dotyczącym natywnych modułów.
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Znajdziesz błąd w React Native?
Prawdziwy fragment kodu, ukryty błąd, jedna próba dziennie. Bez konta, żeby spróbować.

Autor:
Anthony Fillion-MailletZałożyciel SharpSkill
Programista fullstack od ponad 10 lat. Prowadzi SharpSkill i odpowiada za wszystko, co się tu ukazuje.
Zaktualizowano 6 października 2026
Tagi
Udostępnij
Powiązane artykuły

Przewodnik po tworzeniu aplikacji React Native 2026: Produkcyjne aplikacje i pytania rekrutacyjne
Kompletny przewodnik po tworzeniu produkcyjnych aplikacji React Native w 2026 roku obejmujący Expo SDK 56, EAS Build, Hermes V1, Nową Architekturę oraz przygotowanie do rozmów kwalifikacyjnych dla programistów React Native.

Hermes V1 w React Native 0.84: Wydajność, Prekompilowany Bytecode i Pytania Rekrutacyjne
Dogłębna analiza optymalizacji wydajności Hermes V1 w React Native 0.84: prekompilacja bytecode, garbage collector Hades, zarządzanie pamięcią oraz kluczowe pytania rekrutacyjne dla programistów mobilnych.

React Native i TypeScript w 2026: Typobezpieczna architektura i pytania rekrutacyjne
Tworzenie typobezpiecznych aplikacji React Native z TypeScriptem, Codegen, TurboModules i Strict TypeScript API. Obejmuje wzorce architektury, typowaną nawigację, wymagania toolchain dla 0.87 oraz pytania rekrutacyjne.