React Native for Cross-Platform Mobile Development
React Native enables building iOS and Android apps with one codebase. After shipping multiple React Native apps, here’s what works in production.
Why React Native?
Benefits:
- Single codebase - Write once, run on iOS and Android
- Native performance - Uses native components
- Hot reload - Fast development iteration
- Large ecosystem - NPM packages available
Setup
Installation
# Install React Native CLI
npm install -g react-native-cli
# Create new project
react-native init MyApp
# Or use Expo (easier)
npm install -g expo-cli
expo init MyApp
Project Structure
MyApp/
├── App.js
├── package.json
├── android/
│ ├── app/
│ └── build.gradle
├── ios/
│ ├── MyApp/
│ └── Podfile
└── src/
├── components/
├── screens/
├── navigation/
└── services/
Basic Component
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
export default function App() {
return (
<View style={styles.container}>
<Text style={styles.title}>Hello React Native!</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF'
},
title: {
fontSize: 20,
fontWeight: 'bold',
margin: 20
}
});
Navigation
React Navigation
npm install @react-navigation/native
npm install react-native-screens react-native-safe-area-context
npm install @react-navigation/stack
npm install @react-navigation/bottom-tabs
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
function HomeStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
);
}
function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStack} />
<Tab.Screen name="Profile" component={ProfileScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
State Management
Redux Integration
npm install redux react-redux
npm install @reduxjs/toolkit
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import userReducer from './src/redux/userSlice';
const store = configureStore({
reducer: {
user: userReducer
}
});
function App() {
return (
<Provider store={store}>
<NavigationContainer>
{/* Your app */}
</NavigationContainer>
</Provider>
);
}
API Integration
import AsyncStorage from '@react-native-async-storage/async-storage';
class ApiService {
constructor() {
this.baseUrl = 'https://api.example.com';
}
async get(endpoint, headers = {}) {
const token = await AsyncStorage.getItem('authToken');
const response = await fetch(`${this.baseUrl}${endpoint}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
...headers
}
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return response.json();
}
async post(endpoint, data, headers = {}) {
const token = await AsyncStorage.getItem('authToken');
const response = await fetch(`${this.baseUrl}${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
...headers
},
body: JSON.stringify(data)
});
return response.json();
}
}
export default new ApiService();
Native Modules
Calling Native Code
import { NativeModules } from 'react-native';
const { MyNativeModule } = NativeModules;
// Call native method
MyNativeModule.showToast('Hello from native!');
Android Native Module
// MyNativeModule.java
package com.myapp;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import android.widget.Toast;
public class MyNativeModule extends ReactContextBaseJavaModule {
public MyNativeModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "MyNativeModule";
}
@ReactMethod
public void showToast(String message) {
Toast.makeText(getReactApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
}
iOS Native Module
// MyNativeModule.m
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(MyNativeModule, NSObject)
RCT_EXTERN_METHOD(showToast:(NSString *)message)
@end
Performance Optimization
FlatList for Lists
import { FlatList } from 'react-native';
function UserList({ users }) {
return (
<FlatList
data={users}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => <UserItem user={item} />}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={10}
removeClippedSubviews={true}
/>
);
}
Memoization
import React, { memo, useMemo } from 'react';
const UserItem = memo(({ user }) => {
const formattedDate = useMemo(
() => formatDate(user.createdAt),
[user.createdAt]
);
return (
<View>
<Text>{user.name}</Text>
<Text>{formattedDate}</Text>
</View>
);
});
Image Optimization
import { Image } from 'react-native';
<Image
source=
style={styles.image}
resizeMode="cover"
// Use cached images
cache="force-cache"
/>
Platform-Specific Code
import { Platform } from 'react-native';
const styles = StyleSheet.create({
container: {
paddingTop: Platform.OS === 'ios' ? 20 : 0,
...Platform.select({
ios: {
backgroundColor: '#F5F5F5'
},
android: {
backgroundColor: '#FFFFFF'
}
})
}
});
// Platform-specific components
const Button = Platform.select({
ios: () => require('./ButtonIOS').default,
android: () => require('./ButtonAndroid').default
})();
Testing
import { render, fireEvent } from '@testing-library/react-native';
import LoginScreen from './LoginScreen';
test('login button works', () => {
const { getByText, getByPlaceholderText } = render(<LoginScreen />);
const emailInput = getByPlaceholderText('Email');
const passwordInput = getByPlaceholderText('Password');
const loginButton = getByText('Login');
fireEvent.changeText(emailInput, 'user@example.com');
fireEvent.changeText(passwordInput, 'password123');
fireEvent.press(loginButton);
// Assertions
});
Deployment
Android
# Generate release APK
cd android
./gradlew assembleRelease
# Or AAB for Play Store
./gradlew bundleRelease
iOS
# Build for release
cd ios
pod install
xcodebuild -workspace MyApp.xcworkspace \
-scheme MyApp \
-configuration Release \
-archivePath build/MyApp.xcarchive \
archive
Best Practices
- Use TypeScript - Catch errors early
- Optimize images - Compress and resize
- Handle errors - Error boundaries
- Test on devices - Not just simulators
- Monitor performance - Use Flipper/Reactotron
- Code splitting - Lazy load screens
- Use native modules - When needed
- Keep dependencies updated - Security patches
Conclusion
React Native enables:
- Cross-platform development
- Code reuse
- Native performance
- Fast iteration
Start with Expo for easier setup, then eject if you need native modules. The patterns shown here work for production apps.
React Native patterns from May 2018, covering React Native 0.55+ features.