React Native'de navigasyon uzun süre tek bir sorunun etrafında döndü: ekranlar arasındaki geçişi kim yönetecek? React Navigation bu sorunun standart cevabıydı. Expo Router, file-based routing yaklaşımı getirdi ve 2024-2026 arasında hızla olgunlaştı.
Bu yazıda Expo Router'ın 2026 itibarıyla nasıl çalıştığını, React Navigation'dan farkını ve hangi durumda hangisini seçmeniz gerektiğini ele alıyoruz.
Expo Router nedir?
Expo Router, React Native uygulamaları için file-based bir routing sistemidir. Next.js'i biliyorsanız mantık aynı: app/ klasörüne eklediğiniz her dosya otomatik olarak bir route olur. Route yapısını manuel olarak tanımlamanız gerekmez.
Expo Router, React Navigation üzerine inşa edilmiştir. Arka planda aynı navigator'ları kullanır ama bunları sizin yerinize dosya yapısından çıkarır.
Expo Router ile kurulu bir projenin dosya yapısı şöyle görünür:
Expo Router v4: Ne değişti?
Expo Router v4, Expo SDK 52 ile birlikte geldi ve iki önemli değişiklik getirdi.
Typed routes varsayılan olarak açık. Artık href="/abut" gibi yazım hatalarını TypeScript derleme aşamasında yakalar. Link ve router.push() kullanırken route path'leri otomatik olarak tamamlanır.
import { Link } from "expo-router"
<Link href="/abut">About</Link>
<Link href="/about">About</Link>import { Link } from "expo-router"
<Link href="/abut">About</Link>
<Link href="/about">About</Link>import { Link } from "expo-router"
<Link href="/abut">About</Link>
<Link href="/about">About</Link>import { Link } from "expo-router"
<Link href="/abut">About</Link>
<Link href="/about">About</Link>Server components desteği (deneysel). Expo Router v4 ile birlikte React Server Components desteği geldi. Henüz production için önerilmiyor ama API stabilize olmaya başladı.
Yeni proje nasıl başlatılır?
npx create-expo-app@latest --template
npx create-expo-app@latest --template
npx create-expo-app@latest --template
npx create-expo-app@latest --template
Proje oluştuktan sonra:
cd proje-adi
npx expo start
cd proje-adi
npx expo start
cd proje-adi
npx expo start
cd proje-adi
npx expo start
Mevcut bir Expo projesine Expo Router eklemek için:
package.json dosyasında entry point'i belirtin:
{
"main": "expo-router/entry"
}{
"main": "expo-router/entry"
}{
"main": "expo-router/entry"
}{
"main": "expo-router/entry"
}app.json dosyasına scheme ekleyin:
{
"scheme": "uygulama-scheme"
}{
"scheme": "uygulama-scheme"
}{
"scheme": "uygulama-scheme"
}{
"scheme": "uygulama-scheme"
}Tab Navigator nasıl kurulur?
Kök layout app/_layout.tsx:
import { Stack } from "expo-router/stack"
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
</Stack>
)
}import { Stack } from "expo-router/stack"
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
</Stack>
)
}import { Stack } from "expo-router/stack"
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
</Stack>
)
}import { Stack } from "expo-router/stack"
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
</Stack>
)
}Tab layout app/(tabs)/_layout.tsx:
import { Tabs } from "expo-router"
import FontAwesome from "@expo/vector-icons/FontAwesome"
export default function TabLayout() {
return (
<Tabs screenOptions={{ tabBarActiveTintColor: "#bfe031" }}>
<Tabs.Screen
name="index"
options={{
title: "Ana Sayfa",
tabBarIcon: ({ color }) => (
<FontAwesome name="home" size={24} color={color} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
title: "Profil",
tabBarIcon: ({ color }) => (
<FontAwesome name="user" size={24} color={color} />
),
}}
/>
</Tabs>
)
}import { Tabs } from "expo-router"
import FontAwesome from "@expo/vector-icons/FontAwesome"
export default function TabLayout() {
return (
<Tabs screenOptions={{ tabBarActiveTintColor: "#bfe031" }}>
<Tabs.Screen
name="index"
options={{
title: "Ana Sayfa",
tabBarIcon: ({ color }) => (
<FontAwesome name="home" size={24} color={color} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
title: "Profil",
tabBarIcon: ({ color }) => (
<FontAwesome name="user" size={24} color={color} />
),
}}
/>
</Tabs>
)
}import { Tabs } from "expo-router"
import FontAwesome from "@expo/vector-icons/FontAwesome"
export default function TabLayout() {
return (
<Tabs screenOptions={{ tabBarActiveTintColor: "#bfe031" }}>
<Tabs.Screen
name="index"
options={{
title: "Ana Sayfa",
tabBarIcon: ({ color }) => (
<FontAwesome name="home" size={24} color={color} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
title: "Profil",
tabBarIcon: ({ color }) => (
<FontAwesome name="user" size={24} color={color} />
),
}}
/>
</Tabs>
)
}import { Tabs } from "expo-router"
import FontAwesome from "@expo/vector-icons/FontAwesome"
export default function TabLayout() {
return (
<Tabs screenOptions={{ tabBarActiveTintColor: "#bfe031" }}>
<Tabs.Screen
name="index"
options={{
title: "Ana Sayfa",
tabBarIcon: ({ color }) => (
<FontAwesome name="home" size={24} color={color} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
title: "Profil",
tabBarIcon: ({ color }) => (
<FontAwesome name="user" size={24} color={color} />
),
}}
/>
</Tabs>
)
}Dynamic Routes nasıl çalışır?
app/user/[id].tsx dosyası /user/123 ve /user/abc gibi tüm path'leri yakalar:
import { useLocalSearchParams } from "expo-router"
import { View, Text } from "react-native"
export default function UserScreen() {
const { id } = useLocalSearchParams()
return (
<View>
<Text>Kullanıcı ID: {id}</Text>
</View>
)
}import { useLocalSearchParams } from "expo-router"
import { View, Text } from "react-native"
export default function UserScreen() {
const { id } = useLocalSearchParams()
return (
<View>
<Text>Kullanıcı ID: {id}</Text>
</View>
)
}import { useLocalSearchParams } from "expo-router"
import { View, Text } from "react-native"
export default function UserScreen() {
const { id } = useLocalSearchParams()
return (
<View>
<Text>Kullanıcı ID: {id}</Text>
</View>
)
}import { useLocalSearchParams } from "expo-router"
import { View, Text } from "react-native"
export default function UserScreen() {
const { id } = useLocalSearchParams()
return (
<View>
<Text>Kullanıcı ID: {id}</Text>
</View>
)
}Birden fazla segment için app/user/[...slug].tsx catch-all route kullanılır.
Navigation
import { Link, router } from "expo-router"
import { View, Button } from "react-native"
export default function HomeScreen() {
return (
<View>
<Link href="/about">Hakkımızda</Link>
<Link href={{ pathname: "/user/[id]", params: { id: "123" } }}>
Kullanıcı
</Link>
<Button
title="Profile Git"
onPress={() => router.push("/profile")}
/>
<Button
title="Login"
onPress={() => router.replace("/login")}
/>
</View>
)
}import { Link, router } from "expo-router"
import { View, Button } from "react-native"
export default function HomeScreen() {
return (
<View>
<Link href="/about">Hakkımızda</Link>
<Link href={{ pathname: "/user/[id]", params: { id: "123" } }}>
Kullanıcı
</Link>
<Button
title="Profile Git"
onPress={() => router.push("/profile")}
/>
<Button
title="Login"
onPress={() => router.replace("/login")}
/>
</View>
)
}import { Link, router } from "expo-router"
import { View, Button } from "react-native"
export default function HomeScreen() {
return (
<View>
<Link href="/about">Hakkımızda</Link>
<Link href={{ pathname: "/user/[id]", params: { id: "123" } }}>
Kullanıcı
</Link>
<Button
title="Profile Git"
onPress={() => router.push("/profile")}
/>
<Button
title="Login"
onPress={() => router.replace("/login")}
/>
</View>
)
}import { Link, router } from "expo-router"
import { View, Button } from "react-native"
export default function HomeScreen() {
return (
<View>
<Link href="/about">Hakkımızda</Link>
<Link href={{ pathname: "/user/[id]", params: { id: "123" } }}>
Kullanıcı
</Link>
<Button
title="Profile Git"
onPress={() => router.push("/profile")}
/>
<Button
title="Login"
onPress={() => router.replace("/login")}
/>
</View>
)
}Expo Router mu, React Navigation mu?
Her ikisi de production'da güvenilir. Karar genellikle proje tipine göre şekilleniyor.
Expo Router tercih edildiği durumlar: Expo managed workflow kullanıyorsunuz, web desteği gerekiyor, typed routes ve otomatik deep link istiyorsunuz, Next.js gibi file-based routing'e alışkın bir ekip var.
React Navigation tercih edildiği durumlar: Bare workflow veya React Native CLI kullanıyorsunuz, çok özelleşmiş navigator yapısı gerekiyor, mevcut büyük bir proje var ve migration riski yüksek.
Önemli bir not: Expo Router yalnızca Expo projelerinde çalışır. React Native CLI projelerinde React Navigation kullanılması gerekir.
Expo yazısından farkı nedir?
Expo Router, Expo'nun routing katmanıdır. Expo'yu daha önce yazdığımız Expo rehberinde ele aldık: Development Build, EAS, Managed Workflow gibi konular orada. Bu yazı yalnızca navigation ve routing'e odaklanıyor.
Yeni bir Expo projesi başlatıyorsanız Expo Router varsayılan olarak gelir. Ayrıca kurulum gerektirmez.
Sonuç
Expo Router, React Native'de file-based routing'i production'a taşıyan en olgun çözüm. Typed routes ve server components desteğiyle v4, sadece bir routing kütüphanesi olmaktan çıkıp framework katmanına geçiyor.
Yeni bir Expo projesi başlatıyorsanız Expo Router ile başlamak doğru tercih. Mevcut bir React Navigation projeniz varsa migration için acele etmeye gerek yok, ama yeni ekranlar ve özellikler için Expo Router'ı değerlendirmeye değer.