Expo Router ve React Native: Navigasyon Çözümleri Üzerine Bir İnceleme

SHFT
8 min readMar 13, 2024

Expo Router nedir? React Navigation hala standart mı?

Giriş

React Native’de navigasyon, kullanıcıların uygulama içinde farklı ekranlar veya sayfalar arasında geçiş yapabilmesi için kullanılan bir yöntemdir.
Mobil uygulamalarda, kullanıcıların farklı içeriklere erişebilmesi, geri dönebilmesi veya uygulama içinde deep linklerle belirli bir içeriğe yönlendirilmesi gibi işlevleri sağlamak için navigasyon önemlidir. React Native, bu tür navigasyon işlemlerini kolaylaştırmak için birkaç farklı kütüphane ve yöntem sunar.

En Çok Kullanılan Navigasyon Yöntemleri

Oldukça popüler ve güvenilir bir kaynak olan State of React Native 2023 anketinden çıkan sonuçlara göre, Expo Router’ın 2023'te ikinci sıraya geldiğini ve zirvede yer alan React Navigation’dan sonra en popüler ikinci kütüphane haline geldiğini görebiliriz. Biz de bu yazıda Expo Router’ı inceleyip React Navigation’a göre farklarına göz atacağız.

Expo Router

Expo Router, React Native uygulamaları için dosya tabanlı bir yönlendiricidir. React Navigation’dan API olarak bu noktada ayrılır. Uygulamanızdaki ekranlar arasındaki navigasyonu yönetmenize olanak tanır, kullanıcılara uygulamanızın UI’ının farklı bölümleri arasında sorunsuz bir şekilde hareket etme imkanı sunar ve aynı componentleri birden fazla platformda (Android, iOS ve web) kullanır.

Ayrıca hem React Navigation, hem de Expo Router yine Expo takımından çıkan ürünlerdir. Expo Router’ın React Navigation’a göre üstünlük sunduğu ana özellik aslında file-based routing özelliğidir.

Expo Router’ın Sundukları

  • Native Olması: Her ne kadar React Navigation paketinin farklılıklarından bahsetmiş olsak da React Navigation üzerine inşa edildiğini söylebiliriz. Yine bu durumdan dolayı native ve her iki platforma da optimize bir şekilde kullanıma hazırdır.
  • Deep Linking & Share: Uygulama içerisindeki her ekran deep link ile paylaşılmaya hazır halde oluyor.
  • Offline-first Olması: Uygulamaların cachelenmesi ve offline-first olarak koşturulması mevcut, çevrimdışı olunsa bile gelinen URL’leri handle edebiliyor.
  • Evrensellik: Android, iOS ve Web için tek bir navigasyon yapısı kullanılabiliyor.

Expo Router ve React Native CLI Uyumluluğu

Expo Router’ı maalesef sadece Expo Managed projelerde kullanabiliyoruz. Opinionated bir kütüphane olmasından dolayı böyle bir durum olduğunu söyleyebiliriz. React Native CLI projelerinde maalesef başka bir çözüm bulmamız gerekiyor. Alternatif olarak React Navigation’ı kullanabiliriz.

Expo Router Kurulumu

Expo Router kurulumu için iki yol düşünebiliriz:

  • Yeni Uygulama
  • Varolan Uygulama

Yeni uygulamalar için, Expo Router template’ini kullanarak uygulama oluşturabiliriz. Bu, aşağıdaki komut ile mümkündür: npx create-expo-app@latest --template tabs@50. Bu komut, create-expo-app aracını kullanarak, Expo Router ile önceden yapılandırılmış olan 'tabs' şablonuna dayalı bir uygulama oluşturur.

Sonrasında ise

  • cd expo-router-ornegi ile oluşturduğumuz proje dizinine geçiyoruz.
  • npx expo start ile geliştirme sunucumuzu ayağa kaldırıp geliştirme işlemlerine devam edebiliriz. Yine bu ekranda i tuşuna basarak iOS cihazda veya simülatörde uygulamamızı başlatalım.

Varolan Uygulama

Hali hazırda Expo Router olmayan Expo CLI uygulamamıza Expo Router kurmak için şu yolu izleyebiliriz.

  • Gerekli Paketleri Kurmak: npx expo install expo-router react-native-safe-area-context react-native-screens expo-linking expo-constants expo-status-bar komutuyla ihtiyacımız olan bağımlılıkları kurabiliriz.
  • Uygulamaya Giriş Noktası Oluşturmak: Biliyorsunuz ki her uygulamanın bir entry dosyası var, bunu oluşturmak için package.json dosyamızda şu şekilde bir ekleme yapmamız gerekiyor.
{
"main": "expo-router/entry"
}

Bu şekilde giriş dosyamızı app/_layout.js dosyası olarak belirlemiş oluyoruz.

  • Deep Linking Hazırlamak: Uygulamamızda deep link ozelliğini kullanmak için app.json konfigürasyonumuzda bir ekleme yapmalıyız.
{
"scheme": "app-semamiz"
}

Web Gereksinimleri

  • Eğer uygulamamızı web için de geliştireceksek, yine web için React Native’i ve React Dom’u projeye eklememiz gerekiyor.

npx expo install react-native-web react-dom

  • app.json içerisinde Metro bundler için web desteğini de aktif etmeliyiz.
{
"web": {
"bundler": "metro"
}
}
  • Babel konfigürasyonumuzu da yine Expo Router kullanmak için değişmemiz gerekiyor, babel.config.js dosyasında babel-preset-expo’yu ekliyoruz.
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo']
}
}

Son hali bu şekilde de görünebilir.

  • Babel konfigürasyonunu değiştiğimiz için bundler cache’ini silmemiz gerekiyor. npx expo start -c komutuyla cache temizleyerek geliştirme sunucumuzu ayağa kaldırabilir ve devam edebiliriz.

React Navigation’da Tab Navigasyonu

React Navigation ve Expo Router’ı karşılaştıracağımızdan dolayı önce bir standart Tab Bar navigasyonunu her iki kütüphaneyi kullanarak yapacağız. Sonrasında ise Expo Router’ın özelliklerini ve kullanımını gösteren örnekler yapacağız.

Çoğu mobil projede görebileceğimiz tab bar ekran dizilimini şu şekilde belirleyelim.

  • Home
  • Settings
  • Profile
  • Search

Olarak uygulamamızın birkaç ekranı olsun ve bunlar tab bar’da dizilmiş ve navigate edilebilir halde olsun. Bu örneği hem React Navigation’da, hem de Expo Router’da yapıp API’larını karşılaştıralım. Öncelikle daha popüler olan React Navigation ile bu örneğimizi gerçekleştirelim ve çıktılara bakalım.

Öncelikle gerekli paketlerimizi kuracağız. Bunun için şu komutu kullanabiliriz.

npx expo install react-native-screens react-native-safe-area-context @react-navigation/native @react-navigation/bottom-tabs

Dosyalarımızı src/views altında oluşturalım ve index’lerinden dağıtalım.

Sonrasında App.tsx dosyamızda route yapımızı oluşturalım:

import React from "react";

import { StatusBar } from "expo-status-bar";
import { StyleSheet } from "react-native";
import { NavigationContainer } from "@react-navigation/native";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import FontAwesome from "@expo/vector-icons/FontAwesome";

import {
HomeScreen,
ProfileScreen,
SearchScreen,
SettingsScreen,
} from "./src/views";

const Tab = createBottomTabNavigator();

const App = () => {
return (
<NavigationContainer>
<StatusBar style="auto" />
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ color, size }) => {
let iconName: "home" | "search" | "user" | "cog" = "home";
if (route.name === "Search") {
iconName = "search";
} else if (route.name === "Profile") {
iconName = "user";
} else if (route.name === "Settings") {
iconName = "cog";
}
return <FontAwesome name={iconName} size={size} color={color} />;
},
})}
>
<Tab.Screen name={"Home"} component={HomeScreen} />
<Tab.Screen name={"Search"} component={SearchScreen} />
<Tab.Screen name={"Profile"} component={ProfileScreen} />
<Tab.Screen name={"Settings"} component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
});

export default App;

Expo uygulamalarında, built-in kurulu gelen ikon paketleri mevcut, bunları kullanmak için herhangi bir paket kurulumu veya konfigürasyona ihtiyacımız yok. FontAwesome kullanarak ikonlarımızı ekleyebiliriz. ✨

Ve son olarak da çıktımızı paylaşalım. ✨

Expo Router’da Tab Navigasyonu

Expo Router’da app klasörü altına eklediğimiz her ekran, otomatik bir şekilde route olarak da uygulamaya ekleniyor, React Navigation’da ise navigatorler oluşturup ekranlarımızı istediğimiz yapıda kendimiz “manuel” register etmemiz gerekiyor.

Expo Router’da daha once bahsettigimiz gibi, route yapılarının giriş noktaları _layout.js dosyaları oluyor. Belirli route yapılarının içerisinde index yani ilk route’u belirlemek için de index.tsx index.js index.ts gibi index dosyalarına ihtiyacımız var. Bu durumda ise iki adet layout dosyamız olacak, ayrıca tab route yapımızda da home (ana) ekranımızı bir index dosyasında tutacağız.

Uygulamamızın giriş noktası app/_layout.js olarak oluşturulacak.

import { Stack } from "expo-router/stack";

const AppLayout = () => (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
</Stack>
);

export default AppLayout;

Sayfaları tanımlamak için ise app/ dizini altında default export’larla React componentlerimizi tanımlamamız gerekiyor. Örneğin Search ekranı:

import { View, Text, StyleSheet } from "react-native";

const Search: React.FC = () => (
<View style={styles.container}>
<Text>Search</Text>
</View>
);

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
});

export default Search;

React Navigation’da yaptığımız gibi, oluşturduğumuz bu ekranları tabs yapımızın layout dosyasına ekleyelim, her bir route için de ikonlar ekleyelim ki belirleyici olsun ve güzel bir deneyim sunsun. Bu amaçla (tabs)/_layout.tsx dosyamızı oluşturalım.

import React from "react";

import FontAwesome from "@expo/vector-icons/FontAwesome";

import { Tabs } from "expo-router";

const TabLayout = () => (
<Tabs screenOptions={{ tabBarActiveTintColor: "blue" }}>
<Tabs.Screen
name="index"
options={{
title: "Home",
tabBarIcon: ({ color }) => (
<FontAwesome name="home" size={24} color={color} />
),
}}
/>
<Tabs.Screen
name="search"
options={{
title: "Search",
tabBarIcon: ({ color }) => (
<FontAwesome name="search" size={24} color={color} />
),
}}
/>
<Tabs.Screen
name="settings"
options={{
title: "Settings",
tabBarIcon: ({ color }) => (
<FontAwesome name="cog" size={24} color={color} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
title: "Profile",
tabBarIcon: ({ color }) => (
<FontAwesome name="user" size={24} color={color} />
),
}}
/>
</Tabs>
);

export default TabLayout;

En son dosya yapımızın bu şekilde olmasını bekliyoruz:

Ve son olarak da çıktımızı paylaşalım ✨

Aslında Expo Router da arka planda React Navigation kullanıyor, bütün bu tab route yapısında bir API benzerliği yakalayabiliriz. Fakat Expo’nun bize sunduğu belirli özelliklerden ve kendine has dosya bazlı routing sisteminden bahsetmiştik. React Navigation’daki Tabs özelliklerini burada Expo Router’ın bize sunduklarıyla birleştirerek yaptığımızı söyleyebiliriz. Şimdi Expo Router’a özel birkaç durumu ve özelliği farklı senaryolarda örnekleyip, kullanımlarını inceleyelim.

Ekranlar Arası Geçiş

Expo Router’da ekranlar arasında geçiş yapmak için Link’leri kullanacağız. Bu link yapılarını web’deki anchor taglerine, hedefleri ise yine href attribute’larına benzetebiliriz.

import { View } from 'react-native';
import { Link } from 'expo-router';

const Page = () => {
return (
<View>
<Link href="/about">About</Link>
<Link href="/user/bacon">View user</Link>
</View>
);
}

export default Page;

Örneğin bu kod bloğunda kullandığımız Link componentleri, About ekranına gitmek ve bacon id’sine sahip kullanıcıyı barındıran ekrana gitmek için kullanılıyor.

Dinamik Rotalar

Dinamik rotalar bize, uygulama çalışırken path’ler veya URL’ler aracılığıyla sayfanın ilgili veriyi alıp bu veri doğrultusunda davranışını değiştirebilme imkanını sağlar. Popüler bir örnek olarak user’a ait profil ekranlarını gösterebiliriz. Mesela kullanıcı adını yakalayan bir dinamik rota oluşturup bu senaryo üzerinden dinamik rotaları anlayabiliriz.

Diyelim ki her kullanıcının unique bir kullanıcı adı olsun, biz de bu kullanıcı adlarını yakalayan bir dinamik rota oluşturup, user’ın kullanıcı adını gösteren bir ekran yapalım.

Öncelikle bir [user].tsx dosyası hazırlayıp kullanıcı detaylarını göstereceğimiz sayfayı oluşturalım.

import { Stack } from "expo-router/stack";

const AppLayout = () => (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen
name="[user]"
options={{
title: "User",
headerBackTitle: "Back",
}}
/>
</Stack>
);

export default AppLayout;

Sonrasında ise dinamik rotaları kullanmak için profile.tsx dosyamızı hazır hale getirelim.

import { View, Text, StyleSheet, Button } from "react-native";
import { Link } from "expo-router";

const Profile: React.FC = () => (
<View style={styles.container}>
<Link href="/Ahmet" asChild>
<Button title="Ahmet" />
</Link>
<Link href="/Ali" asChild>
<Button title="Ali" />
</Link>
<Link href="/Mehmet" asChild>
<Button title="Mehmet" />
</Link>
</View>
);

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
});

export default Profile;

Profil sayfamızdan bu şekilde Ali butonuna basıp ilgili user ekranına gideceğiz.

Ve çıktımızı gösterelim ✨

Sonuç

Bu yazımızda, React Native’deki navigasyon/routing çözümlerinden bahsettik. Expo Router özelliklerini açıkladık. Expo Router ve React Navigation’ı benzer senaryolar için ele aldık. File-based routing’i kullanarak yapabileceklerimizden de Expo Router üzerinde bir örnek gösterdik.

İhtiyaçlarımıza göre en popüler bu iki çözüm arasından seçimlerimizi yapabilir, uygulamalarımızı en optimize ve ölçeklenebilir halde hazırlayabiliriz.

Gelecek yazılarda görüşmek dileğiyle, teşekkürler 🙂

Barbaros Affan Yıldırım | Frontend Developer

--

--