Navigasi adalah cara pengguna berpindah antarlayar. Di web ada URL dan tombol back browser. Di aplikasi mobile gak ada address bar: layar ditumpuk seperti kartu, tab ada di bagian bawah, dan cara kembalinya beda di Android dan iOS. Bayangin meja kasir Bu Rini. Di atas daftar order, kamu menaruh nota #13 untuk dibaca (push). Selesai membaca, kamu ambil lagi nota teratas itu (pop), dan daftarnya masih ada di bawah, persis di posisi tadi. Di meja yang sama ada dua laci, Pesanan dan Akun. Pindah laci gak menumpuk apa-apa. Tumpukan nota itu stack, laci itu tab.
🧪 Tebakan cepat: Mbak Wulan membuka aplikasi Laundry Bu Rini di HP Android, mengetuk order #13, lalu menekan tombol back Android dua kali. Apa yang terjadi di tekanan kedua? Jawabannya ada di bagian "Back di Android dan iOS".
Stack dan tab
- Stack menumpuk layar: membuka layar baru berarti push ke atas tumpukan, kembali berarti pop dari atas. Layar di atas punya header dengan tombol back.
- Tab menampilkan bar berisi beberapa layar utama, biasanya di bawah. Pindah tab gak menumpuk layar, dan tiap tab menyimpan keadaannya sendiri.
- Drawer adalah menu yang ditarik dari samping. Aplikasi Bu Rini gak memakainya.
Aplikasi Bu Rini memakai keduanya: stack di luar, berisi tab (Pesanan dan Akun) dan layar Detail order. Keadaan navigasinya disimpan sebagai object biasa. Setelah order #13 dibuka:
{ index: 1, routes: [
{ name: 'Tabs', state: { index: 0, routes: [{ name: 'Pesanan' }, { name: 'Akun' }] } },
{ name: 'DetailOrder', params: { id: '13' } },
] }
routesadalah tumpukannya, dari bawah ke atas.index: 1artinya layar teratas, DetailOrder, yang tampil.params: { id: '13' }adalah data yang dibawa ke layar itu: cuma nomor order-nya.
React Navigation 7
React Navigation adalah library navigasi React Native yang paling mapan: sampai Expo Router 55 pun, Expo Router dibangun di atasnya. Di npm, 2 sampai 8 Oktober 2026, @react-navigation/native diunduh 5,98 juta kali dan expo-router 7,29 juta kali (unduhan, bukan jumlah aplikasi). Versi stabilnya 7 (@react-navigation/native 7.5.0 per Oktober 2026), versi 8 masih alpha. Di project Expo tanpa Expo Router (npx create-expo-app@latest --template blank):
npm install @react-navigation/native @react-navigation/native-stack @react-navigation/bottom-tabs
npx expo install react-native-screens react-native-safe-area-context
Dokumentasinya merekomendasikan static API: navigator ditulis sebagai object.
// App.js
import { createStaticNavigation, useNavigation } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Pressable, Text, View } from 'react-native';
import { ORDERS } from './src/data/orders';
function PesananScreen() {
const navigation = useNavigation();
return (
<View>
{ORDERS.map((order) => (
<Pressable key={order.id} onPress={() => navigation.navigate('DetailOrder', { id: order.id })}>
<Text>#{order.id} {order.nama}</Text>
</Pressable>
))}
</View>
);
}
function DetailOrderScreen({ route }) {
const order = ORDERS.find((o) => o.id === route.params.id);
return <Text>{order.nama}: {order.status}</Text>;
}
const AkunScreen = () => <Text>Akun</Text>;
const Tabs = createBottomTabNavigator({
screens: {
Pesanan: { screen: PesananScreen, options: { title: 'Pesanan saya', tabBarLabel: 'Pesanan' } },
Akun: AkunScreen,
},
});
const RootStack = createNativeStackNavigator({
screens: {
Tabs: { screen: Tabs, options: { headerShown: false } },
DetailOrder: {
screen: DetailOrderScreen,
options: ({ route }) => ({ title: `Order #${route.params.id}` }),
},
},
});
const Navigation = createStaticNavigation(RootStack);
export default function App() {
return <Navigation />;
}
navigation.navigate('DetailOrder', { id: order.id })push layar DetailOrder dengan paramid. Untuk order #13,route.paramsberisi{ id: '13' }.- Param cukup ID-nya. Data lengkapnya dicari dari
ORDERS, jadi gak ada salinan yang bisa basi. - Dynamic API menulis hal yang sama sebagai komponen:
<NavigationContainer>,<Stack.Navigator>,<Stack.Screen name="DetailOrder" component={DetailOrderScreen} />. Lebih banyak kode, tapi daftar layarnya bisa berubah saat aplikasi jalan.
Expo Router: layar dari nama file
Project dari pelajaran setup memakai Expo Router, yang membuat rute dari file di src/app. Versinya 57, mengikuti nomor SDK. Sejak versi 56 (Mei 2026), Expo Router gak lagi bergantung pada React Navigation: bagian yang dipakainya di-fork, dan import dari @react-navigation/* di kode aplikasi gak didukung lagi. Import dari expo-router.
src/app/
├── _layout.tsx # Stack
├── (tabs)/
│ ├── _layout.tsx # Tabs: Pesanan dan Akun
│ ├── index.tsx # / daftar order
│ └── akun.tsx # /akun
└── order/
└── [id].tsx # /order/13 detail order
// src/app/order/[id].tsx
import { Stack, useLocalSearchParams } from 'expo-router';
import { Text, View } from 'react-native';
import { ORDERS } from '@/data/orders';
export default function DetailOrder() {
const { id } = useLocalSearchParams<{ id: string }>();
const order = ORDERS.find((o) => o.id === id);
return (
<View>
<Stack.Screen options={{ title: `Order #${id}` }} />
<Text>{order ? `${order.nama}: ${order.status}` : 'Order gak ketemu'}</Text>
</View>
);
}
(tabs)adalah grup: tanda kurungnya gak ikut di alamat.[id]adalah bagian alamat yang berubah-ubah.- Dari daftar,
router.push('/order/13')atau<Link href="/order/13">membuka layar ini, danuseLocalSearchParams()mengembalikan{ id: '13' }. Param dari alamat selalu string.
Back di Android dan iOS
- iOS gak punya tombol back sistem. Kembali lewat tombol di kiri atas header, atau geser dari tepi kiri layar. Di layar pertama gak ada yang bisa dituju, dan aplikasinya tetap terbuka.
- Android punya tombol atau gestur back sistem. React Navigation memanggil
goBack()selama masih ada layar untuk di-pop. Di layar pertama, tekanan back diteruskan ke Android, dan aplikasinya keluar ke home screen. - Di tab, back Android dari tab Akun kembali dulu ke tab Pesanan, karena
backBehaviorbawaan tab adalahfirstRoute.
| Aksi | Tumpukan stack | Yang tampil |
|---|---|---|
| Buka aplikasi | Tabs | Pesanan saya |
| Ketuk #13 | Tabs, DetailOrder { id: '13' } |
Order #13 |
| Back Android | Tabs | Pesanan saya |
| Back Android | gak ada yang bisa di-pop | keluar ke home screen |
Jadi tekanan kedua menutup aplikasinya ke home screen. Itu jawaban tebakannya.
⚠️ Jebakan yang sering ketemu
- Mengirim seluruh object order sebagai param. Kalau status #13 berubah, layar detail tetap memegang salinan lama. Kirim
{ id: '13' }, lalu cari datanya. - Membandingkan param dengan angka.
useLocalSearchParams()memberi'13', jadiid === 13selalufalse. Bandingkan dengan string. - navigate ke layar yang sama dengan param lain. Dari Order #13,
navigation.navigate('DetailOrder', { id: '12' })cuma mengganti param layar yang sedang tampil, gak menumpuk. Pakainavigation.push(...)kalau mau layar baru di atasnya. - Import
@react-navigation/*di project Expo Router 56 ke atas. Gak didukung. AmbilStack,Link, danrouterdariexpo-router, danTabsdariexpo-router/js-tabs: di Expo Router 57,Tabsdariexpo-routersudah deprecated.
TL;DR: Stack menumpuk layar (push untuk membuka, pop untuk kembali), tab berpindah antarlayar utama tanpa menumpuk. React Navigation 7 adalah library navigasi React Native yang paling mapan, dengan static API (createNativeStackNavigator({ screens }), createStaticNavigation) yang direkomendasikan dokumentasinya. Kirim param seminimal mungkin: navigation.navigate('DetailOrder', { id: '13' }), dibaca dari route.params.id. Expo Router 57 membuat rute dari file: src/app/order/[id].tsx adalah /order/13, dibaca dengan useLocalSearchParams(), dan sejak versi 56 gak bergantung lagi pada React Navigation. iOS kembali lewat header atau geser dari tepi kiri. Android punya back sistem, dan di layar pertama back mengeluarkan aplikasi ke home screen.