React Native 2026: Expo vs CLI + React Navigation Complete 🚀
React Native powers 85% of top mobile apps with Expo (managed workflow, OTA updates) vs React Native CLI (full native control). React Navigation v7 delivers native stack, tabs, drawer, deep linking, and TypeScript-first navigation. Production-ready with EAS Build, CodePush, and Fastlane.
🎯 Expo vs React Native CLI Decision Matrix
| Feature | Expo (Managed) | Expo (Bare) | React Native CLI |
|---|---|---|---|
| Setup Time | 5 mins | 10 mins | 30-60 mins |
| Native Modules | Expo SDK only | All | All |
| OTA Updates | Yes | Yes | CodePush |
| Custom Native | ❌ | Yes | Yes |
| Build Service | EAS Build | EAS/Local | Local/Fastlane |
| Team Size | Solo/Small | Medium | Enterprise |
| Learning Curve | Easy | Medium | Hard |
🚀 1. EXPO MANAGED WORKFLOW (Recommended)
Quick Start
# Create Expo app
npx create-expo-app@latest MyApp --template
cd MyApp
# Core dependencies
npx expo install react-native-safe-area-context react-native-screens expo-linking expo-constants expo-status-bar
npx expo install @react-navigation/native @react-navigation/native-stack @react-navigation/bottom-tabs
npx expo install react-native-gesture-handler react-native-reanimated
App Entry (App.tsx)
// App.tsx - Expo + React Navigation v7
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { RootSiblingParent } from 'react-native-root-siblings';
import HomeScreen from './src/screens/HomeScreen';
import ProfileScreen from './src/screens/ProfileScreen';
const Stack = createNativeStackNavigator();
export default function App() {
return (
<RootSiblingParent>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
</RootSiblingParent>
);
}
Home Screen with Navigation
// src/screens/HomeScreen.tsx
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { useNavigation } from '@react-navigation/native';
export default function HomeScreen() {
const navigation = useNavigation<any>();
return (
<View style={styles.container}>
<Text style={styles.title}>Welcome to Expo! 🚀</Text>
<Button
title="Go to Profile"
onPress={() => navigation.navigate('Profile', { userId: '123' })}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
title: { fontSize: 24, fontWeight: 'bold', marginBottom: 20 },
});
🛠️ 2. REACT NAVIGATION V7 + TABS + DRAWER
Complete Navigator Setup
// App.tsx - Tabs + Stack + Drawer
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createDrawerNavigator } from '@react-navigation/drawer';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { Ionicons } from '@expo/vector-icons';
const Tab = createBottomTabNavigator();
const Drawer = createDrawerNavigator();
const Stack = createNativeStackNavigator();
// Tab Navigator
function TabNavigator() {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ color, size }) => {
let iconName: keyof typeof Ionicons.glyphMap;
if (route.name === 'Home') iconName = 'home';
else if (route.name === 'Profile') iconName = 'person';
return <Ionicons name={iconName} size={size} color={color} />;
},
headerShown: false,
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
}
// Main App
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Main"
component={TabNavigator}
options={{ headerShown: false }}
/>
<Stack.Screen name="Auth" component={AuthScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
📱 3. REACT NATIVE CLI (Full Control)
# Global CLI
npm install -g @react-native-community/cli
# Create project
npx react-native@latest init MyApp --version 0.75
cd MyApp
# Navigation dependencies
npm install @react-navigation/native @react-navigation/native-stack @react-navigation/bottom-tabs
npm install react-native-screens react-native-safe-area-context
cd ios && pod install && cd ..
Metro Config (CLI)
// metro.config.js
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const config = {};
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
🌐 4. EXPO ROUTER (File-Based Navigation)
# Expo Router (Next.js-style)
npx create-expo-app@latest MyApp --template
npx expo install expo-router
app/ ├── _layout.tsx # Root layout ├── index.tsx # Home screen (/) ├── profile.tsx # /profile └── (tabs)/ ├── _layout.tsx # Tab navigator ├── home.tsx └── profile.tsx
// app/_layout.tsx
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: 'Home' }} />
<Stack.Screen name="profile" options={{ title: 'Profile' }} />
</Stack>
);
}
🔐 5. AUTHENTICATION FLOW
// Auth Navigator
function AuthNavigator() {
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
</Stack.Navigator>
);
}
// Root Navigator with Auth Check
function RootNavigator() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{ headerShown: false }}>
{isAuthenticated ? (
<Stack.Screen name="MainTabs" component={TabNavigator} />
) : (
<Stack.Screen name="Auth" component={AuthNavigator} />
)}
</Stack.Navigator>
</NavigationContainer>
);
}
📲 6. PRODUCTION FEATURES
Push Notifications (Expo)
npx expo install expo-notifications expo-device
// notifications.ts
import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});
EAS Build (Production)
# Login to Expo
npx eas login
# Configure build
npx eas build:configure
# Build for stores
eas build --platform all --profile preview
# eas.json
{
"build": {
"preview": {
"distribution": "internal",
"ios": { "simulator": true }
},
"production": {}
}
}
🎨 7. UI LIBRARY INTEGRATION
# NativeWind (Tailwind CSS)
npx expo install nativewind
npx expo install react-native-reanimated react-native-gesture-handler
# shadcn-native (UI Components)
npx shadcn-native-ui@latest init
npx shadcn-native-ui@latest add button card
// NativeWind components
import { StyledView, StyledText } from 'nativewind';
<StyledView className="flex-1 justify-center items-center bg-gray-100 p-8">
<StyledText className="text-2xl font-bold text-blue-600 mb-4">
NativeWind + Expo
</StyledText>
<StyledTouchableOpacity className="bg-blue-500 px-8 py-3 rounded-lg">
<StyledText className="text-white font-semibold">Button</StyledText>
</StyledTouchableOpacity>
</StyledView>
📊 Performance Comparison
| Metric | Expo Managed | Expo Bare | React Native CLI |
|---|---|---|---|
| Build Time | 3 mins | 5 mins | 15 mins |
| APK Size | 25MB | 18MB | 15MB |
| Hot Reload | Instant | Instant | 2-3s |
| OTA Updates | Yes | Yes | Manual |
| Native Access | Limited | Full | Full |
🎯 Production Checklist
✅ [] Expo SDK 51 / React Native 0.75 ✅ [] React Navigation v7 (Stack + Tabs + Drawer) ✅ [] TypeScript full-stack ✅ [] NativeWind / shadcn-native UI ✅ [] Expo Router (file-based, optional) ✅ [] EAS Build + OTA updates ✅ [] Push notifications ✅ [] Deep linking ✅ [] Detox testing ✅ [] Sentry crash reporting
🚀 DEPLOYMENT COMMANDS
# Expo (Recommended)
eas build --platform all --profile production
eas submit --platform ios
eas submit --platform android
# CLI (Manual)
npx react-native run-android --variant=release
cd ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release
🎯 Final Strategy
Solo/Small Teams → Expo Managed (80%) Medium Teams → Expo Bare (15%) Enterprise → React Native CLI (5%)
Expo + React Navigation v7 = Production mobile apps in 1 day. File-based routing, TypeScript, OTA updates, EAS Build = 90% less native complexity.
Expo: expo.dev | React Navigation: reactnavigation.org