react-native
Expert React Native mobile development for iOS and Android. Covers Expo vs bare workflow, navigation patterns, performance optimization, native modules, and platform-specific code.
React Native Mobile Development
Expert guidance for building production-ready React Native applications. Covers architecture decisions, performance optimization, navigation, state management, and native integration.
Project Setup and Workflow Selection
BAD: Starting with bare workflow without justification
npx react-native init MyApp
# Adds unnecessary native complexity from day one
# Requires Xcode/Android Studio setup immediately
GOOD: Start with Expo, eject only when needed
npx create-expo-app@latest MyApp --template blank-typescript
cd MyApp
# Expo provides:
# - Instant dev environment (no Xcode/Android Studio)
# - OTA updates via EAS
# - Prebuild for native customization
# - Standard libraries (camera, location, etc.)
# When you need custom native code:
npx expo prebuild
# Generates ios/ and android/ directories
# Maintains Expo libraries, adds native flexibility
When to use bare workflow:
- Heavy native module customization required
- Existing native iOS/Android codebase integration
- Libraries incompatible with Expo (rare in 2026)
Navigation Architecture
BAD: Navigation without type safety or deep linking
// No type checking on navigation params
function HomeScreen({ navigation }) {
return (
<Button
title="Go to Profile"
onPress={() => navigation.navigate('Profile', { userId: '123' })}
// Typo in route name causes runtime crash
/>
);
}
GOOD: React Navigation with TypeScript and deep linking
// types/navigation.ts
import { NavigationProp } from '@react-navigation/native';
export type RootStackParamList = {
Home: undefined;
Profile: { userId: string };
Settings: { section?: 'privacy' | 'notifications' };
};
export type AppNavigation = NavigationProp<RootStackParamList>;
// App.tsx
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
const Stack = createNativeStackNavigator<RootStackParamList>();
const linking = {
prefixes: ['myapp://', 'https://myapp.com'],
config: {
screens: {
Home: '',
Profile: 'user/:userId',
Settings: 'settings/:section?',
},
},
};
export default function App() {
return (
<NavigationContainer linking={linking}>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
// HomeScreen.tsx
import { useNavigation } from '@react-navigation/native';
import type { AppNavigation } from './types/navigation';
function HomeScreen() {
const navigation = useNavigation<AppNavigation>();
return (
<Button
title="Go to Profile"
onPress={() => navigation.navigate('Profile', { userId: '123' })}
// TypeScript validates route name and params
/>
);
}
Tab navigation pattern:
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Ionicons } from '@expo/vector-icons';
const Tab = createBottomTabNavigator();
function TabNavigator() {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
const iconName = route.name === 'Home' ? 'home' : 'settings';
return <Ionicons name={iconName} size={size} color={color} />;
},
tabBarActiveTintColor: '#d4943a',
tabBarInactiveTintColor: 'gray',
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
);
}
List Performance Optimization
BAD: ScrollView for long lists, no optimization
function ProductList({ products }) {
return (
<ScrollView>
{products.map(product => (
<ProductCard key={product.id} product={product} />
// Renders ALL items immediately
// No virtualization, causes memory issues
))}
</ScrollView>
);
}
function ProductCard({ product }) {
const styles = {
container: { padding: 16 }, // Inline styles
};
return <View style={styles.container}>...</View>;
// Re-creates style object on every render
}
GOOD: FlatList with full optimization
import { FlatList, StyleSheet } from 'react-native';
import { memo } from 'react';
const ITEM_HEIGHT = 100;
function ProductList({ products }) {
const renderItem = ({ item }) => <ProductCard product={item} />;
const keyExtractor = (item) => item.id;
const getItemLayout = (data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
});
return (
<FlatList
data={products}
renderItem={renderItem}
keyExtractor={keyExtractor}
getItemLayout={getItemLayout}
// Skip measurement, instant scroll
windowSize={10}
// Render 10 screen heights worth of items
maxToRenderPerBatch={10}
updateCellsBatchingPeriod={50}
// Batch rendering for smooth scroll
removeClippedSubviews={true}
// Unmount off-screen items (Android optimization)
/>
);
}
const ProductCard = memo(({ product }) => {
return (
<View style={styles.container}>
<Text style={styles.title}>{product.name}</Text>
<Text style={styles.price}>${product.price}</Text>
</View>
);
});
// React.memo prevents unnecessary re-renders
const styles = StyleSheet.create({
container: { padding: 16, height: ITEM_HEIGHT },
title: { fontSize: 16, fontWeight: 'bold' },
price: { fontSize: 14, color: '#666' },
});
// StyleSheet.create optimizes style objects
<!-- See references/advanced.md for extended examples -->