Pernahkah kamu mau bikin aplikasi mobile tapi batal karena ribet setup Android Studio? Saya dulu juga gitu. Download Android Studio 5GB, tunggu Gradle sync 20 menit, lalu build pertama gagal karena SDK version gak cocok. Frustrasi banget.
Nah, kalau kamu mau bikin aplikasi Android dan iOS tanpa drama itu semua, jawabannya adalah React Native dengan Expo. Saya udah pakai kombinasi ini untuk beberapa project kecil sampai menengah, dan pengalaman developer-nya jauh lebih enak dibanding native development murni.
Apa Itu Expo dan Kenapa Pakai Itu?
Expo itu sekumpulan tools yang nge-wrap React Native biar kamu gak perlu repot sama konfigurasi native Android dan iOS. Singkatnya: kamu fokus nulis kode JavaScript/TypeScript, Expo urus sisanya - mulai dari build, test di device, sampai publish ke Play Store dan App Store.
Bedanya dengan React Native CLI (bare workflow)? React Native CLI butuh kamu install Android Studio, Xcode, konfigurasi Gradle, CocoaPods, dan semua dependency native secara manual. Expo CLI pakai managed workflow di mana semua itu di-abstraksi. Kamu butuh library native tertentu? Pakai expo install dan selesai.
- Expo Go: Aplikasi untuk test langsung di device tanpa build. Scan QR code, aplikasi langsung ke-load di HP kamu.
- EAS (Expo Application Services): Cloud build service. Kamu gak butuh Mac untuk build iOS - EAS build di cloud.
- Expo Router: File-based routing seperti Next.js. Bikin navigasi multi-screen jauh lebih simpel.
- Over-the-Air (OTA) Updates: Update JavaScript bundle tanpa publish ulang ke Play Store. Fix bug production dalam hitungan menit.
Persiapan Environment Development
Sebelum mulai, pastikan kamu punya Node.js versi 18 atau lebih baru. Cek versinya:
node --version
# v18.19.0 atau lebih baru
npm --version
# v10.x atau lebih baru
Install Expo CLI secara global:
npm install -g expo-cli
# atau pakai npx tanpa install global
npx create-expo-app my-first-app
Setelah selesai, masuk ke direktori project dan start development server:
cd my-first-app
npx expo start
Terminal bakal nunjukin QR code. Download aplikasi Expo Go dari Play Store atau App Store, scan QR code tersebut, dan aplikasi kamu langsung muncul di HP. Gak perlu kabel data, gak perlu USB debugging, gak perlu ADB. Cukup HP dan laptop di jaringan WiFi yang sama.
Struktur Project Expo Modern
Project Expo modern pakai struktur file-based routing. Kalau kamu pernah pakai Next.js, konsepnya mirip banget:
my-first-app/
|-- app/
| |-- _layout.tsx # Root layout, bungkus semua screen
| |-- index.tsx # Screen utama (route: /)
| |-- about.tsx # Screen about (route: /about)
| |-- profile/
| |-- [id].tsx # Dynamic route (route: /profile/123)
|-- assets/
| |-- images/
| |-- fonts/
|-- app.json # Konfigurasi Expo (nama app, icon, dll)
|-- package.json
|-- tsconfig.json
Setiap file .tsx di dalam folder app/ otomatis jadi route. Nama file menentukan URL path. Ini jauh lebih intuitif dibanding Stack.Navigator + Stack.Screen di React Navigation versi lama.
Membuat Screen Pertama: Todo List App
Mari kita bikin aplikasi todo list sederhana untuk paham flow-nya. Edit file app/index.tsx:
import { useState } from 'react';
import { Text, View, TextInput, TouchableOpacity, FlatList, StyleSheet } from 'react-native';
type Todo = {
id: string;
text: string;
done: boolean;
};
export default function HomeScreen() {
const [todos, setTodos] = useState<Todo[]>([]);
const [input, setInput] = useState('');
const addTodo = () => {
if (!input.trim()) return;
setTodos([...todos, {
id: Date.now().toString(),
text: input.trim(),
done: false
}]);
setInput('');
};
const toggleTodo = (id: string) => {
setTodos(todos.map(t =>
t.id === id ? { ...t, done: !t.done } : t
));
};
const deleteTodo = (id: string) => {
setTodos(todos.filter(t => t.id !== id));
};
return (
<View style={styles.container}>
<Text style={styles.title}>My Todos</Text>
<View style={styles.inputRow}>
<TextInput
style={styles.input}
value={input}
onChangeText={setInput}
placeholder="Tambah tugas baru..."
onSubmitEditing={addTodo}
/>
<TouchableOpacity style={styles.addButton} onPress={addTodo}>
<Text style={styles.addButtonText}>+</Text>
</TouchableOpacity>
</View>
<FlatList
data={todos}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<View style={styles.todoItem}>
<TouchableOpacity
style={{ flex: 1 }}
onPress={() => toggleTodo(item.id)}
>
<Text style={[
styles.todoText,
item.done && styles.todoDone
]}>
{item.text}
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => deleteTodo(item.id)}>
<Text style={styles.deleteButton}>Hapus</Text>
</TouchableOpacity>
</View>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 20, backgroundColor: '#f5f5f5' },
title: { fontSize: 28, fontWeight: 'bold', marginBottom: 20, marginTop: 40 },
inputRow: { flexDirection: 'row', marginBottom: 20 },
input: {
flex: 1, borderWidth: 1, borderColor: '#ddd',
borderRadius: 8, paddingHorizontal: 12, paddingVertical: 10,
backgroundColor: '#fff', fontSize: 16
},
addButton: {
backgroundColor: '#007AFF', justifyContent: 'center',
alignItems: 'center', width: 44, height: 44,
borderRadius: 8, marginLeft: 8
},
addButtonText: { color: '#fff', fontSize: 24, fontWeight: 'bold' },
todoItem: {
flexDirection: 'row', alignItems: 'center',
backgroundColor: '#fff', padding: 16, borderRadius: 8,
marginBottom: 8, borderWidth: 1, borderColor: '#eee'
},
todoText: { fontSize: 16, flex: 1 },
todoDone: { textDecorationLine: 'line-through', color: '#999' },
deleteButton: { color: '#FF3B30', fontWeight: '600' }
});
Simpan file tersebut, dan aplikasi di Expo Go bakal auto-reload. Coba tambah beberapa todo, klik untuk toggle done, dan hapus. Semua berjalan real-time tanpa perlu rebuild.
Navigasi Multi-Screen dengan Expo Router
Aplikasi mobile yang useful biasanya punya lebih dari satu screen. Bikin file app/about.tsx untuk halaman About:
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import { router } from 'expo-router';
export default function AboutScreen() {
return (
<View style={styles.container}>
<Text style={styles.title}>Tentang App Ini</Text>
<Text style={styles.description}>
Todo List app dibuat dengan React Native + Expo.
Belajar mobile development bisa dari mana saja.
</Text>
<TouchableOpacity style={styles.button} onPress={() => router.back()}>
<Text style={styles.buttonText}>Kembali</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 20, backgroundColor: '#f5f5f5' },
title: { fontSize: 24, fontWeight: 'bold', marginBottom: 12, marginTop: 40 },
description: { fontSize: 16, color: '#555', lineHeight: 24, marginBottom: 30 },
button: {
backgroundColor: '#007AFF', paddingVertical: 12,
paddingHorizontal: 24, borderRadius: 8, alignItems: 'center'
},
buttonText: { color: '#fff', fontSize: 16, fontWeight: '600' }
});
Lalu tambahkan tombol navigasi di app/index.tsx:
import { router } from 'expo-router';
// Di dalam JSX, tambahkan:
<TouchableOpacity onPress={() => router.push('/about')}>
<Text style={{ color: '#007AFF', textAlign: 'center', marginTop: 20 }}>
Tentang App
</Text>
</TouchableOpacity>
Navigasi antar screen pakai router.push() dan router.back(). Sama persis seperti Next.js. Gak perlu setup Stack Navigator, gak perlu import library tambahan, gak perlu konfigurasi manual.
Mengakses Fitur Native: Camera dan Lokasi
Salah satu keunggulan Expo adalah akses ke hardware native yang gampang. Mau akses kamera? Cukup install package dan minta permission:
# Install camera dan location
npx expo install expo-camera expo-location
Bikin file app/scan.tsx untuk scan barcode:
import { useState, useEffect } from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import { CameraView, useCameraPermissions } from 'expo-camera';
export default function ScanScreen() {
const [permission, requestPermission] = useCameraPermissions();
const [scanned, setScanned] = useState(false);
const [result, setResult] = useState('');
if (!permission) return <View style={styles.container} />;
if (!permission.granted) {
return (
<View style={styles.container}>
<Text>Butuh akses kamera untuk scan</Text>
<Button title="Izinkan" onPress={requestPermission} />
</View>
);
}
return (
<View style={styles.container}>
<CameraView
style={styles.camera}
onBarcodeScanned={scanned ? undefined : ({ data }) => {
setScanned(true);
setResult(data);
}}
/>
{scanned && (
<View style={styles.result}>
<Text style={styles.resultText}>Hasil: {result}</Text>
<Button title="Scan Lagi" onPress={() => setScanned(false)} />
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
camera: { flex: 1 },
result: { padding: 20, backgroundColor: 'rgba(0,0,0,0.7)' },
resultText: { color: '#fff', fontSize: 16, marginBottom: 10 }
});
Expo otomatis handle permission request. Di Android, permission ditambahkan ke app.json secara otomatis saat kamu jalankan expo install. Di iOS, permission string muncul di Info.plist tanpa kamu sentuh file native-nya.
Build dan Deploy ke Play Store dengan EAS
Ini bagian yang paling sering bikin orang mundur dari mobile development: build APK/AAB untuk Play Store. Dengan EAS, prosesnya jadi 3 command:
# 1. Install EAS CLI
npm install -g eas-cli
# 2. Login ke akun Expo
eas login
# 3. Configure project (jalankan sekali)
eas build:configure
# 4. Build APK untuk testing
eas build --platform android --profile preview
# 5. Build AAB untuk Play Store
eas build --platform android --profile production
EAS build di cloud, jadi kamu gak perlu punya spek komputer tinggi atau install Android SDK. Build biasanya selesai dalam 10-15 menit, tergantung antrian. Hasilnya langsung bisa didownload atau di-push ke Play Store.
Untuk App Store iOS, kamu butuh Apple Developer account (99 USD per tahun). Tapi yang keren: kamu gak perlu Mac. EAS build di server macOS milik Expo, jadi kamu bisa build iOS dari laptop Windows atau Linux.
OTA Updates: Update Tanpa Publish Ulang
Bayangin kamu nemu typo di production. Di native development, kamu harus build ulang, submit ke Play Store, tunggu review 2-3 hari, baru user dapet update. Dengan Expo OTA, kamu bisa push update JavaScript bundle langsung ke user dalam hitungan detik:
# Configure OTA updates
eas update:configure
# Push update ke production
eas update --branch production --message "fix: typo di halaman about"
User yang buka aplikasi akan otomatis download versi terbaru. Kode native (bila ada perubahan) tetap butuh build ulang, tapi fix bug JavaScript bisa langsung tanpa tunggu Play Store review. Ini game-changer banget untuk iterasi cepat.
Performance Tips: Jangan Bikin App yang Laggy
Setelah aplikasi mulai tumbuh, performance jadi penting. Beberapa tips yang saya pelajari dari pengalaman:
1. Pakai FlatList, bukan ScrollView untuk list panjang. FlatList virtualize items - cuma render yang visible di layar. ScrollView render semua, yang bikin memory bengkak saat list tumbuh.
2. Hindari inline functions di renderItem. Bikin function di luar atau pakai useCallback supaya gak re-render setiap frame:
// BAD: re-render setiap frame
renderItem={({ item }) => (
<TodoItem todo={item} onPress={() => toggleTodo(item.id)} />
)}
// GOOD: pakai useCallback
const handleToggle = useCallback((id: string) => {
setTodos(prev => prev.map(t =>
t.id === id ? { ...t, done: !t.done } : t
));
}, []);
renderItem={({ item }) => (
<TodoItem todo={item} onPress={() => handleToggle(item.id)} />
)}
3. Optimize image dengan expo-image. Pakai expo-image sebagai pengganti Image bawaan. Expo Image punya caching built-in, blurhash support, dan lazy loading:
npx expo install expo-image
import { Image } from 'expo-image';
<Image
source={{ uri: 'https://example.com/photo.jpg' }}
style={{ width: 200, height: 200 }}
placeholder={{ blurhash: 'LEHV6nWB2yk8pyo0adRm.' }}
contentFit="cover"
transition={300}
/>
Kapan Harus Pilih Expo vs React Native CLI (Bare)?
Expo managed workflow menutupi 90% use case. Tapi ada skenario di mana kamu butuh bare workflow:
- Butuh module native custom yang gak ada di Expo Modules: Misalnya SDK payment tertentu yang butuh native Java/Kotlin. Kalau begini, kamu bisa eject atau pakai
expo prebuilduntuk generate native project. - Butuh kontrol penuh atas native code: Kalau kamu tim dengan Android/iOS developer yang mau custom implementasi di level native, bare workflow lebih cocok.
- App size concern: Expo Go menambahkan runtime di sekitar 20-30MB. Untuk production build dengan EAS, ini tidak jadi masalah karena yang di-bundle hanya library yang kamu pakai. Tapi kalau target device entry-level dengan storage terbatas, tetap perlu dipertimbangkan.
Realitanya, Expo udah mature banget sekarang. Bahkan app production besar seperti Discord, Bluesky, dan Coinbase pakai Expo. Untuk pemula sampai intermediate, pilih managed workflow sampai ada alasan spesifik untuk eject.
Kesimpulan
React Native + Expo bikin mobile development jadi accessible untuk siapa saja yang sudah bisa JavaScript. Kamu gak perlu belajar Kotlin, Swift, atau setup Android Studio. Cukup install Node.js, jalankan satu command, dan aplikasi langsung live di HP kamu.
Untuk deploy ke Play Store, EAS Build handle semuanya di cloud. OTA updates bikin iterasi cepat tanpa tunggu review. Expo Router bikin navigasi multi-screen sesimpel bikin file baru. Dan kalau butuh akses native seperti kamera, lokasi, atau sensor - semuanya tinggal expo install.
Kalau kamu udah pernah bikin web app dengan React, transisi ke mobile dengan Expo feels natural banget. Saya personally pribadi pindah dari native Android development ke Expo untuk project personal karena iterasi cepat dan gak perlu pusing dengan native build tools.
Kamu udah pernah coba Expo atau masih ragu? Topik mobile development apa lagi yang mau kamu eksplor? Coba bikin todo list app di atas dan share pengalamanmu di kolom komentar!