React Native'de Expo Router: Dosya Tabanlı Navigasyon Rehberi

Expo Router ile React Native navigasyonunu öğrenin: dosya tabanlı yönlendirme, layoutlar, dinamik rotalar, tipli navigasyon ve modal, sekme gibi gelişmiş desenler.

React Native'de Expo Router — dosya tabanlı navigasyon

Expo Router, React Native uygulamalarına dosya tabanlı yönlendirme getirerek manuel navigasyon yapılandırmasını Next.js'den ilham alan konvansiyon tabanlı bir yaklaşımla değiştiriyor. Expo SDK 57 ve Expo Router ile birlikte Android, iOS ve web platformları için çapraz platform navigasyonu oluşturmak, dosyaları doğru dizinde oluşturmaktan ibaret hale geliyor.

Hızlı Başlangıç

Yeni Expo projeleri, Expo Router önceden yapılandırılmış olarak gelir. Dosya tabanlı yönlendirme ile başlamak için npx create-expo-app@latest --template default komutunu çalıştırmak yeterlidir. Mevcut projelere expo-router paketi kurulup giriş noktası güncellenerek eklenebilir.

Expo Router'da Dosya Tabanlı Yönlendirme Nasıl Çalışır

app dizini içindeki her dosya otomatik olarak bir rota haline gelir. Dosya yolu doğrudan URL yoluna eşlenir ve merkezi bir navigasyon yapılandırması ihtiyacını ortadan kaldırır. app/settings.tsx dosyası /settings rotasını oluştururken, app/profile/edit.tsx dosyası /profile/edit rotasına eşlenir.

Bu yaklaşım, geleneksel React Navigation kurulumuna kıyasla üç temel avantaj sunar:

  • Sıfır yapılandırma: rotalar dosya oluşturulduğu anda var olur
  • Otomatik deep linking: her ekran bir URL alır, paylaşım ve test imkanı sağlar
  • Tip güvenli navigasyon: TypeScript, derleme zamanında hangi rotaların mevcut olduğunu bilir
app/index.tsxtypescript
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 },
})

Link bileşeni tüm platformlarda navigasyonu yönetir. Web'de SEO için uygun href özelliklerine sahip bir <a> etiketi olarak render edilir. Yerel platformlarda ise yığın tabanlı navigasyonu tetikler.

Proje Yapısı ve Layout Dosyaları

Expo Router, navigasyon konteynerlerini tanımlamak için _layout.tsx dosyalarını kullanır. Her dizin kendi layout dosyasına sahip olabilir ve iç içe geçmiş navigasyon hiyerarşileri oluşturulabilir. Kök layout tüm uygulamayı sararken, iç içe geçmiş layout'lar belirli bölümleri kontrol eder.

Tipik bir proje yapısı şu şekildedir:

text
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/privacy

Rota grupları — parantez içine alınmış dizinler — URL'yi etkilemeden dosyaları düzenler. Yukarıdaki (tabs) dizini bir sekme navigatörü oluşturur, ancak URL'ler /tabs/home yerine /home, /search ve /profile olarak kalır.

app/_layout.tsxtypescript
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>
  )
}

Kök layout aynı zamanda fontların yüklenmesi, sağlayıcıların başlatılması ve genel ayarların yapılandırılması için kullanılan yerdir.

Expo Router ile Sekme Navigasyonu Oluşturma

Sekme navigasyonu, bir rota grubu içinde _layout.tsx dosyası gerektirir. Expo Router, platforma özgü sekme deneyimleri için hem NativeTabs bileşenini hem de çoğu kullanım senaryosunu karşılayan standart Tabs bileşenini sunar. SDK 56'dan itibaren Native Tabs, sekmenin görünür kalırken seçilmesini engelleyen disabled özelliğini destekler.

app/(tabs)/_layout.tsxtypescript
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>
  )
}

Her sekme ekranı dosyası standart bir React bileşeni dışa aktarır. Sekme çubuğu simgesi, etiketi ve rozeti, layout dosyasındaki options özelliği aracılığıyla yapılandırılır.

Dinamik Rotalar ve Rota Parametreleri

Dinamik segmentler, dosya adında köşeli parantez kullanır. [id].tsx adlı bir dosya tek bir segmentle eşleşirken, [...slug].tsx kalan tüm segmentleri yakalar.

app/product/[id].tsxtypescript
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' },
})

/product/42 adresine gidildiğinde bu ekran id değeri "42" olarak render edilir. useLocalSearchParams hook'u, tüm rota parametrelerine tipli erişim sağlar.

Tüm segmentleri yakalayan (catch-all) rotalar için [...slug].tsx dosyası kullanılır:

app/docs/[...slug].tsxtypescript
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('/')} />
}

React Native mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Derleme Zamanı Güvenliği İçin Tipli Rotalar

Expo Router, typed routes özelliği etkinleştirildiğinde rota tiplerini otomatik olarak oluşturur. Bu sayede bozuk bağlantılar çalışma zamanı yerine derleme zamanında tespit edilir.

Tipli rotaları app.json dosyasında etkinleştirmek için:

json
{
  "expo": {
    "experiments": {
      "typedRoutes": true
    }
  }
}

Etkinleştirildikten sonra Link bileşenindeki href özelliği ve router.push() argümanı yalnızca geçerli rota dizelerini kabul eder:

app/checkout.tsxtypescript
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')
}

Tipli rotalar, useLocalSearchParams ile mükemmel uyum sağlar. Oluşturulan tipler, rota tanımı ile bu rotayı kullanan bileşen arasındaki parametre adlarının eşleşmesini garanti eder ve yalnızca belirli bir ekrana gidildiğinde ortaya çıkacak ince hataları önler.

Expo Router'da modaller, layout dosyasında presentation: 'modal' ile yapılandırılmış normal ekranlardır. Bu yaklaşım dosya tabanlı konvansiyonu korur — modal yalnızca başka bir rotadır.

app/_layout.tsxtypescript
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>
  )
}
app/create-post.tsxtypescript
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,
  },
})

/create-post adresine gidildiğinde modal sunum tetiklenir. router.back() çağrısı modali kapatarak yığındaki önceki ekrana döner.

Programatik Navigasyon ve Router API

Link bileşeninin ötesinde, Expo Router, router nesnesi aracılığıyla imperatif bir API sunar. Bu API, kullanıcı dokunuşları yerine iş mantığı tarafından tetiklenen navigasyonu yönetir.

typescript
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
}

router.push ve router.replace arasındaki fark, React Native kimlik doğrulama akışlarında önem kazanır. Başarılı bir giriş sonrasında router.replace('/dashboard') kullanılması, kullanıcının giriş ekranına geri dönmesini engeller.

Navigasyon ve Yönlendirme

router.replace() navigasyon geçmişindeki mevcut girdiyi değiştirir. Kimliği doğrulanmamış kullanıcıları yönlendiren koruma mekanizmalarında, bileşen render'ı içinde <Redirect href="/login" /> kullanılmalıdır — render aşamasında tetiklenir ve sunucu tarafı render ile doğru şekilde çalışır.

Middleware ve Rota Koruması

Expo Router, rota düzeyinde mantık için sunucu middleware'i sunar. +middleware.ts dosyası, istekleri rota bileşenine ulaşmadan önce yakalar ve kimlik doğrulama kontrolleri, yönlendirmeler ve başlık manipülasyonu sağlar.

app/+middleware.tstypescript
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
}

Sunucu middleware'inin çalışmadığı yerel platformlarda rota koruması istemci tarafı korumalarına dayanır. Yaygın bir desen, korunan layout'ları kimlik doğrulama kontrolüyle sarar:

app/(authenticated)/_layout.tsxtypescript
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 />
}
Platform Farkındalığı

Sunucu middleware'i yalnızca sunucu tarafı render ile web'de çalışır. Yerel uygulamalar için layout bileşenlerinde her zaman istemci tarafı rota korumaları uygulanmalıdır. Her iki yaklaşımın birleştirilmesi, tüm platformlarda tutarlı koruma sağlar.

SDK 56 Geçişi: React Navigation Fork'u

Expo SDK 56 ile birlikte Expo Router artık @react-navigation/* paketlerine bağımlı değildir. Proje doğrudan React Navigation'dan import ediyorsa, import'lar güncellenmelidir:

Orijinal PaketYeni Konum
@react-navigation/nativeexpo-router/react-navigation
@react-navigation/stackexpo-router/js-stack
@react-navigation/bottom-tabsexpo-router/js-tabs

Import'ları otomatik olarak güncellemek için codemod kullanılabilir:

bash
npx expo-codemod sdk-56-expo-router-react-navigation-replace src

Çalışma zamanı API'si değişmeden kalır. Yalnızca modül belirteçleri taşınmıştır. @react-navigation/core paketinden import eden üçüncü taraf kütüphaneler otomatik olarak çalışır, çünkü Expo CLI bu import'ları paketleme sırasında yeniden yazar.

Kaynaklar

Mülakatlarda Expo Router Hakkında Beklenenler

  • Expo Router, app dizinindeki her dosyayı otomatik olarak bir rotaya eşler ve manuel navigasyon yapılandırmasını ortadan kaldırır
  • _layout.tsx dosyaları ile tanımlanan layout'lar, yığın, sekme ve çekmece navigasyon hiyerarşileri oluşturur
  • [param].tsx ile dinamik rotalar ve [...slug].tsx ile catch-all rotalar, tam TypeScript desteğiyle parametreli navigasyonu yönetir
  • Tipli rotalar, app.json dosyasında etkinleştirildiğinde bozuk navigasyon bağlantılarını derleme zamanında yakalar
  • Modal ekranlar, router.push/replace/back ile programatik navigasyon ve sunucu middleware'i yönlendirme araç setini tamamlar
  • Parantezli dizinlerle oluşturulan rota grupları, URL'leri etkilemeden kodu düzenler
  • Layout bileşenlerindeki istemci tarafı rota korumaları yerel platformlarda kimlik doğrulamayı yönetirken, sunucu middleware'i web'i kapsar
  • SDK 56'dan itibaren @react-navigation/* import'ları expo-router/* karşılıklarına güncellenmelidir

React Native mimarisi ve mülakat hazırlığı hakkında daha fazla bilgi için native modüller modülüne bakılabilir.

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Günün meydan okuması

React Native kodundaki hatayı bulabilir misin?

Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Anthony Fillion-Maillet

Yazan:

Anthony Fillion-Maillet

SharpSkill kurucusu

10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.

6 Ekim 2026 tarihinde güncellendi

Etiketler

#react-native
#expo
#expo-router
#navigation
#mobile-development
#tutorial

Paylaş

İlgili makaleler