Expo Router di React Native: Panduan Lengkap Navigasi Berbasis File
Expo Router menghadirkan sistem routing berbasis file ke React Native, terinspirasi dari Next.js. Panduan lengkap mencakup konfigurasi, navigasi tab, rute dinamis, modal, middleware, dan proteksi rute.

Expo Router mengubah cara pengelolaan navigasi dalam aplikasi React Native dengan mengadopsi sistem routing berbasis file yang terinspirasi langsung dari Next.js. Sejak hadirnya SDK Expo 55 dan Expo Router v6, konfigurasi navigator secara manual tidak lagi diperlukan. Cukup dengan membuat sebuah file di dalam direktori app, layar baru langsung tersedia sebagai rute. Android, iOS, dan web didukung secara seragam tanpa konfigurasi tambahan. Pendekatan ini secara signifikan menekan kompleksitas proyek lintas platform dan mempercepat siklus pengembangan aplikasi mobile.
Proyek Expo baru sudah menyertakan Expo Router secara default. Perintah npx create-expo-app@latest --template default@sdk-55 menghasilkan proyek yang telah dikonfigurasi dengan routing berbasis file. Untuk proyek yang sudah ada, cukup instal package expo-router dan perbarui entry point aplikasi.
Cara Kerja Routing Berbasis File di Expo Router
Setiap file yang ditempatkan di dalam direktori app secara otomatis menjadi sebuah rute. Jalur file berkorespondensi langsung dengan jalur URL, sehingga menghilangkan kebutuhan akan file konfigurasi terpusat. File app/settings.tsx menciptakan rute /settings, sedangkan app/profile/edit.tsx berkorespondensi dengan /profile/edit.
Konvensi ini menawarkan tiga keunggulan utama dibandingkan konfigurasi tradisional menggunakan React Navigation:
- Tanpa konfigurasi: rute terbentuk secara otomatis begitu file dibuat
- Deep linking otomatis: setiap layar memiliki URL tersendiri, memudahkan proses berbagi dan pengujian
- Navigasi bertipe: TypeScript mengenali rute yang tersedia sejak tahap kompilasi
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 },
})Komponen Link menangani navigasi di semua platform. Di web, komponen ini menghasilkan elemen anchor dengan atribut href yang sesuai untuk keperluan SEO. Di platform native, komponen ini memicu navigasi bertipe stack.
Struktur Proyek dan File Layout
Expo Router menggunakan file _layout.tsx untuk mendefinisikan kontainer navigasi. Setiap direktori dapat memiliki layout-nya sendiri, sehingga memungkinkan terciptanya hierarki navigasi bersarang. Layout root membungkus keseluruhan aplikasi, sementara layout bersarang mengontrol bagian-bagian tertentu.
Berikut adalah struktur proyek yang umum digunakan:
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/privacyGrup rute -- direktori yang diapit tanda kurung -- berfungsi untuk mengelompokkan file tanpa memengaruhi URL. Direktori (tabs) di atas membentuk navigator tab, namun URL tetap menjadi /home, /search, dan /profile, bukan /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>
)
}Layout root juga berperan sebagai titik inisialisasi untuk pemuatan font, pengaturan provider, dan konfigurasi global lainnya. File ini menggantikan App.tsx tradisional sebagai entry point aplikasi.
Membangun Navigasi Tab
Navigasi tab memerlukan file _layout.tsx di dalam sebuah grup rute. Expo Router v6 memperkenalkan NativeTabs untuk pengalaman native yang spesifik per platform, namun komponen standar Tabs sudah mencakup sebagian besar kebutuhan.
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>
)
}Setiap file layar tab mengekspor komponen React standar. Ikon, label, dan badge pada tab bar dikonfigurasi melalui prop options di layout.
Rute Dinamis dan Parameter Rute
Segmen dinamis menggunakan tanda kurung siku pada nama file. File bernama [id].tsx mencocokkan satu segmen, sedangkan [...slug].tsx menangkap seluruh segmen yang tersisa pada jalur URL.
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' },
})Mengakses /product/42 akan menampilkan layar ini dengan variabel id bernilai "42". Hook useLocalSearchParams menyediakan akses bertipe terhadap seluruh parameter rute.
Untuk rute catch-all, [...slug].tsx menangkap segmen jalur secara keseluruhan:
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('/')} />
}Siap menguasai wawancara React Native Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Typed Routes: Keamanan pada Tahap Kompilasi
Expo Router secara otomatis menghasilkan tipe rute ketika fitur typed routes diaktifkan. Pemeriksaan ini mendeteksi tautan yang rusak pada tahap kompilasi, bukan saat aplikasi dijalankan.
Aktivasi dilakukan melalui file app.json:
{
"expo": {
"experiments": {
"typedRoutes": true
}
}
}Setelah diaktifkan, prop href pada komponen Link dan argumen pada router.push() hanya menerima string yang sesuai dengan rute yang valid:
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')
}Fitur typed routes bekerja sangat baik dengan useLocalSearchParams. Tipe yang dihasilkan menjamin kecocokan antara nama parameter pada definisi rute dan parameter yang digunakan oleh komponen pemanggil, sehingga mencegah bug halus yang baru muncul saat navigasi ke layar tertentu.
Layar Modal dan Opsi Presentasi
Modal di Expo Router merupakan layar biasa yang dikonfigurasi dengan presentation: 'modal' pada layout. Pendekatan ini tetap menghormati konvensi routing berbasis file: modal pada dasarnya hanyalah sebuah rute seperti rute lainnya.
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,
},
})Mengakses /create-post akan memicu presentasi modal. Pemanggilan router.back() menutup modal dan mengembalikan pengguna ke layar sebelumnya dalam tumpukan navigasi.
Navigasi Programatik dan API Router
Selain komponen Link, Expo Router menyediakan API imperatif melalui objek router. API ini memungkinkan pengelolaan navigasi yang dipicu oleh logika bisnis, bukan hanya oleh interaksi langsung pengguna.
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
}Perbedaan antara router.push dan router.replace sangat krusial dalam alur autentikasi. Setelah login berhasil, router.replace('/dashboard') mencegah pengguna kembali ke layar login melalui tombol kembali.
router.replace() menggantikan entri saat ini dalam riwayat navigasi. Untuk guard autentikasi yang mengalihkan pengguna yang belum login, disarankan menggunakan <Redirect href="/login" /> di dalam render komponen. Pendekatan ini bekerja pada fase render dan berfungsi dengan baik bersama server-side rendering di web.
Middleware dan Proteksi Rute
Expo Router v6 memperkenalkan middleware server untuk logika yang diterapkan pada level rute. File +middleware.ts mencegat permintaan sebelum mencapai komponen rute, memungkinkan pemeriksaan autentikasi, pengalihan, dan manipulasi header HTTP.
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
}Pada platform native, middleware server tidak dijalankan. Proteksi rute mengandalkan guard di sisi klien. Pola yang paling umum digunakan adalah membungkus layout yang dilindungi dengan pemeriksaan autentikasi:
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 server hanya berjalan di web dengan server-side rendering. Untuk aplikasi native, implementasi guard di sisi klien pada komponen layout bersifat wajib. Mengombinasikan kedua pendekatan ini menjamin proteksi yang konsisten di seluruh platform.
Kesimpulan
Expo Router v6 membawa pendekatan navigasi ke ekosistem React Native yang menyelaraskan pengembangan mobile dengan konvensi yang telah ditetapkan oleh framework web modern. Berikut adalah poin-poin utama yang perlu diperhatikan:
- Expo Router v6 menggantikan konfigurasi navigasi manual dengan konvensi berbasis file, di mana setiap file dalam direktori
appsecara otomatis menjadi sebuah rute - Layout yang didefinisikan melalui file
_layout.tsxmembentuk hierarki navigasi -- stack, tab, dan drawer -- tanpa memerlukan file konfigurasi terpusat - Rute dinamis dengan
[param].tsxdan rute catch-all dengan[...slug].tsxmenangani navigasi berparameter dengan dukungan TypeScript yang menyeluruh - Fitur typed routes mendeteksi tautan navigasi yang rusak pada tahap kompilasi saat fitur ini diaktifkan melalui
app.json - Layar modal, navigasi programatik melalui
router.push/replace/back, dan middleware server melengkapi perangkat routing yang tersedia - Grup rute menggunakan direktori bertanda kurung mengelompokkan kode tanpa memengaruhi URL, menjaga struktur direktori tetap rapi seiring pertumbuhan aplikasi
- Guard di sisi klien pada layout menangani autentikasi di platform native, sementara middleware server mencakup web -- mengombinasikan keduanya menjamin cakupan lintas platform yang menyeluruh
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Tag
Bagikan
Artikel terkait

React Native: Membangun Aplikasi Mobile Lengkap di Tahun 2026
Panduan lengkap pengembangan aplikasi mobile iOS dan Android dengan React Native. Dari persiapan lingkungan hingga publikasi, semua dasar yang Anda perlukan untuk memulai.

Angular Standalone Components: Panduan Migrasi dan Praktik Terbaik 2026
Panduan lengkap migrasi Angular standalone components. Langkah-langkah menghapus NgModules, mengaktifkan lazy loading, dan mengadopsi standalone API di Angular 21.