Expo Router w React Native: Kompletny przewodnik po nawigacji opartej na plikach
Kompletny przewodnik po Expo Router w React Native — nawigacja oparta na plikach, trasy dynamiczne, zakładki, modale i ochrona tras w 2026 roku.

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. Od wersji Expo SDK 55 i Expo Router v6, 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@sdk-55, 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ń — zastępując tradycyjny plik App.tsx jako punkt wejścia.
Budowanie nawigacji zakładkowej z Expo Router
Nawigacja zakładkowa wymaga pliku _layout.tsx wewnątrz grupy tras. Expo Router v6 wprowadza NativeTabs dla doświadczeń zakładkowych specyficznych dla platformy, ale standardowy komponent Tabs z Expo Router sprawdza się w większości przypadków.
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. 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 v6 wprowadza 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.
Podsumowanie
- Expo Router v6 zastępuje ręczną konfigurację nawigacji konwencjami opartymi na plikach, automatycznie mapując każdy plik w katalogu
appna trasę - Layouty definiowane przez pliki
_layout.tsxtworzą hierarchie nawigacji — stosy, zakładki i szuflady — bez centralnej konfiguracji - 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, utrzymując czystą strukturę plików w miarę rozwoju aplikacji
- Zabezpieczenia tras po stronie klienta w layoutach obsługują uwierzytelnianie na platformach natywnych, a middleware serwerowe pokrywa sieć web — oba podejścia łącznie zapewniają ochronę na wszystkich platformach
Tagi
Udostępnij
