Introduction
React Native is a popular framework developed by Facebook that allows developers to build mobile applications using React and JavaScript. The beauty of React Native lies in its “learn once, write anywhere” philosophy – if you know React, you can leverage that knowledge to build native mobile apps for both iOS and Android platforms. Let’s learn the React Native basics in the coming lines.
What is React Native?
React Native is an open-source mobile application framework that enables developers to use React along with native platform capabilities. Unlike hybrid mobile apps that run in a WebView, React Native apps compile to native code, providing better performance and a more authentic user experience.
Key Features of React Native
- Cross-Platform Development: Write once, run on both iOS and Android
- Native Performance: Compiles to native code for optimal performance
- Hot Reloading: See changes instantly without rebuilding the entire app
- Large Community: Extensive ecosystem and community support
- Code Reusability: Share code between web and mobile applications
- Native Modules: Access to device-specific features through native modules
React vs React Native: Key Differences
Component Differences
React (Web) | React Native |
<div> | <View> |
<span>, <p> | <Text> |
<img> | <Image> |
<input> | <TextInput> |
<button> | <TouchableOpacity> |
Styling Differences
React Native uses a subset of CSS properties and implements styling through JavaScript objects:
// React (Web) - CSS
.container {
display: flex;
justify-content: center;
align-items: center;
background-color: #f0f0f0;
}
// React Native - StyleSheet
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f0f0f0',
},
});
Setting Up React Native Development Environment
Prerequisites
- Node.js (version 12 or higher)
- React Native CLI or Expo CLI
- Android Studio (for Android development)
- Xcode (for iOS development – macOS only)
Installation Options
Option 1: Expo CLI (Recommended for Beginners)
npm install -g expo-cli
expo init MyFirstApp
cd MyFirstApp
expo start
Option 2: React Native CLI
npm install -g @react-native-community/cli
npx react-native init MyFirstApp
cd MyFirstApp
For Android:
npx react-native run-android
For iOS:
npx react-native run-ios
Basic React Native Components
View Component
The View component is the fundamental building block, similar to div in HTML:
import React from 'react';
import { View, StyleSheet } from 'react-native';
const BasicView = () => {
return (
<View style={styles.container}>
<View style={styles.box} />
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
box: {
width: 100,
height: 100,
backgroundColor: 'blue',
},
});
export default BasicView;
Text Component
All text must be wrapped in a Text component:
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const TextExample = () => {
return (
<View style={styles.container}>
<Text style={styles.title}>Hello React Native!</Text>
<Text style={styles.subtitle}>This is a subtitle</Text>
<Text style={styles.body}>
This is body text that can span multiple lines and
will wrap automatically based on the container width.
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 10,
},
subtitle: {
fontSize: 18,
color: '#666',
marginBottom: 15,
},
body: {
fontSize: 16,
lineHeight: 24,
},
});
export default TextExample;
Image Component
Display images from various sources:
import React from 'react';
import { View, Image, StyleSheet } from 'react-native';
const ImageExample = () => {
return (
<View style={styles.container}>
{/* Local Image */}
<Image
source={require('./assets/logo.png')}
style={styles.localImage}
/>
{/* Network Image */}
<Image
source={{uri: 'https://example.com/image.jpg'}}
style={styles.networkImage}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
padding: 20,
},
localImage: {
width: 100,
height: 100,
marginBottom: 20,
},
networkImage: {
width: 200,
height: 150,
resizeMode: 'cover',
},
});
export default ImageExample;
Handling User Input
TextInput Component
import React, { useState } from 'react';
import { View, TextInput, Text, StyleSheet } from 'react-native';
const InputExample = () => {
 const [text, setText] = useState('');
 const [email, setEmail] = useState('');
 return (
  <View style={styles.container}>
   <Text style={styles.label}>Name:</Text>
   <TextInput
    style={styles.input}
    value={text}
    onChangeText={setText}
    placeholder="Enter your name"
   />
  Â
   <Text style={styles.label}>Email:</Text>
   <TextInput
    style={styles.input}
    value={email}
    onChangeText={setEmail}
    placeholder="Enter your email"
    keyboardType="email-address"
    autoCapitalize="none"
   />
  Â
   <Text style={styles.output}>
    Hello {text}, your email is {email}
   </Text>
  </View>
 );
};
const styles = StyleSheet.create({
 container: {
  flex: 1,
  padding: 20,
 },
 label: {
  fontSize: 16,
  fontWeight: 'bold',
  marginTop: 20,
  marginBottom: 5,
 },
 input: {
  borderWidth: 1,
  borderColor: '#ddd',
  padding: 10,
  borderRadius: 5,
  fontSize: 16,
 },
 output: {
  marginTop: 20,
  fontSize: 16,
  color: '#333',
 },
});
export default InputExample;
Touchable Components
React Native provides several components for handling touch interactions:
import React from 'react';
import {
View,
Text,
TouchableOpacity,
TouchableHighlight,
TouchableWithoutFeedback,
Alert,
StyleSheet
} from 'react-native';
const TouchableExample = () => {
const handlePress = (type) => {
Alert.alert('Pressed!', `You pressed the ${type} button`);
};
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.button}
onPress={() => handlePress('TouchableOpacity')}
>
<Text style={styles.buttonText}>TouchableOpacity</Text>
</TouchableOpacity>
<TouchableHighlight
style={styles.button}
onPress={() => handlePress('TouchableHighlight')}
underlayColor="#0056b3"
>
<Text style={styles.buttonText}>TouchableHighlight</Text>
</TouchableHighlight>
<TouchableWithoutFeedback
onPress={() => handlePress('TouchableWithoutFeedback')}
>
<View style={styles.button}>
<Text style={styles.buttonText}>TouchableWithoutFeedback</Text>
</View>
</TouchableWithoutFeedback>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
padding: 20,
},
button: {
backgroundColor: '#007bff',
padding: 15,
borderRadius: 5,
marginVertical: 10,
alignItems: 'center',
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
export default TouchableExample;
Lists in React Native
FlatList Component
For rendering large lists efficiently:
import React from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native';
const ListExample = () => {
const data = [
{ id: '1', name: 'Apple', color: 'Red' },
{ id: '2', name: 'Banana', color: 'Yellow' },
{ id: '3', name: 'Orange', color: 'Orange' },
{ id: '4', name: 'Grape', color: 'Purple' },
{ id: '5', name: 'Kiwi', color: 'Green' },
];
const renderItem = ({ item }) => (
<View style={styles.item}>
<Text style={styles.itemName}>{item.name}</Text>
<Text style={styles.itemColor}>{item.color}</Text>
</View>
);
return (
<View style={styles.container}>
<Text style={styles.title}>Fruit List</Text>
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id}
showsVerticalScrollIndicator={false}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
},
item: {
backgroundColor: '#f9f9f9',
padding: 15,
marginVertical: 5,
borderRadius: 5,
flexDirection: 'row',
justifyContent: 'space-between',
},
itemName: {
fontSize: 18,
fontWeight: 'bold',
},
itemColor: {
fontSize: 16,
color: '#666',
},
});
export default ListExample;
Styling in React Native
StyleSheet API
React Native uses the StyleSheet API for styling:
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const StylingExample = () => {
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerText}>Header</Text>
</View>
<View style={styles.content}>
<Text style={styles.contentText}>Content Area</Text>
</View>
<View style={[styles.box, styles.redBox]}>
<Text style={styles.boxText}>Red Box</Text>
</View>
<View style={[styles.box, styles.blueBox]}>
<Text style={styles.boxText}>Blue Box</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
header: {
backgroundColor: '#333',
padding: 20,
alignItems: 'center',
},
headerText: {
color: 'white',
fontSize: 20,
fontWeight: 'bold',
},
content: {
flex: 1,
padding: 20,
justifyContent: 'center',
alignItems: 'center',
},
contentText: {
fontSize: 18,
marginBottom: 20,
},
box: {
width: 100,
height: 100,
margin: 10,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 10,
},
redBox: {
backgroundColor: 'red',
},
blueBox: {
backgroundColor: 'blue',
},
boxText: {
color: 'white',
fontWeight: 'bold',
},
});
export default StylingExample;
Navigation in React Native
React Navigation
Install React Navigation:
npm install @react-navigation/native
npm install react-native-screens react-native-safe-area-context
For stack navigation:
npm install @react-navigation/stack
npm install react-native-gesture-handler
Basic navigation setup:
import React from 'react';
const HomeScreen = ({ navigation }) => {
return (
<View style={styles.screen}>
<Text style={styles.title}>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details', { name: 'John Doe' })}
/>
</View>
);
};
const DetailsScreen = ({ route, navigation }) => {
const { name } = route.params;
return (
<View style={styles.screen}>
<Text style={styles.title}>Details Screen</Text>
<Text style={styles.text}>Hello, {name}!</Text>
<Button
title="Go Back"
onPress={() => navigation.goBack()}
/>
</View>
);
};
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
const styles = StyleSheet.create({
screen: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
},
text: {
fontSize: 18,
marginBottom: 20,
},
});
export default App;
State Management in React Native
React Native uses the same state management concepts as React:
import React, { useState, useEffect } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
const CounterApp = () => {
const [count, setCount] = useState(0);
const [isRunning, setIsRunning] = useState(false);
useEffect(() => {
let interval;
if (isRunning) {
interval = setInterval(() => {
setCount(prevCount => prevCount + 1);
}, 1000);
}
return () => clearInterval(interval);
}, [isRunning]);
const handleStart = () => setIsRunning(true);
const handleStop = () => setIsRunning(false);
const handleReset = () => {
setCount(0);
setIsRunning(false);
};
return (
<View style={styles.container}>
<Text style={styles.counter}>{count}</Text>
<View style={styles.buttonContainer}>
<Button
title={isRunning ? "Stop" : "Start"}
onPress={isRunning ? handleStop : handleStart}
/>
<Button title="Reset" onPress={handleReset} />
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5f5f5',
},
counter: {
fontSize: 48,
fontWeight: 'bold',
marginBottom: 40,
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
width: '60%',
},
});
export default CounterApp;
Platform-Specific Code
Handle platform differences using the Platform API:
import React from 'react';
import { View, Text, Platform, StyleSheet } from 'react-native';
const PlatformExample = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>
Running on: {Platform.OS}
</Text>
<View style={styles.platformBox}>
<Text style={styles.platformText}>
{Platform.OS === 'ios' ? 'iOS Specific Feature' : 'Android Specific Feature'}
</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
text: {
fontSize: 18,
marginBottom: 20,
},
platformBox: {
padding: 20,
backgroundColor: Platform.OS === 'ios' ? '#007AFF' : '#4CAF50',
borderRadius: 10,
},
platformText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
// Platform-specific styles
const platformStyles = StyleSheet.create({
container: {
...Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
});
export default PlatformExample;
Debugging React Native Apps
Debug Tools and Techniques
- React Native Debugger: Standalone debugging tool
- Chrome Developer Tools: For JavaScript debugging
- Flipper: Desktop debugging platform by Facebook
- Console Logging: Use console.log() for quick debugging
import React, { useEffect } from 'react';
import { View, Text } from 'react-native';
const DebugExample = () => {
useEffect(() => {
console.log('Component mounted');
console.warn('This is a warning');
console.error('This is an error for testing');
// Debug network requests
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
.then(data => {
console.log('API Response:', data);
})
.catch(error => {
console.error('API Error:', error);
});
}, []);
return (
<View>
<Text>Check the debugger console for logs</Text>
</View>
);
};
export default DebugExample;
Best Practices for React Native Development
Performance Optimization
- Use FlatList for large datasets
- Optimize images – use appropriate formats and sizes
- Minimize re-renders – use React.memo, useMemo, useCallback
- Remove console statements in production builds
Code Organization
src/
├── components/
│ ├── common/
│ └── screens/
├── navigation/
├── services/
├── utils/
├── styles/
└── constants/
Error Handling
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.log('Error caught by boundary:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<View style={styles.errorContainer}>
<Text style={styles.errorText}>Something went wrong.</Text>
</View>
);
}
return this.props.children;
}
}
const styles = StyleSheet.create({
errorContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
errorText: {
fontSize: 18,
color: 'red',
},
});
export default ErrorBoundary;
Complete Example: Todo App
Here’s a simple Todo app demonstrating various React Native concepts:
import React, { useState } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
FlatList,
Alert,
StyleSheet,
} from 'react-native';
const TodoApp = () => {
const [todos, setTodos] = useState([]);
const [inputText, setInputText] = useState('');
const addTodo = () => {
if (inputText.trim() === '') {
Alert.alert('Error', 'Please enter a todo item');
return;
}
const newTodo = {
id: Date.now().toString(),
text: inputText.trim(),
completed: false,
};
setTodos([...todos, newTodo]);
setInputText('');
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
const deleteTodo = (id) => {
Alert.alert(
'Delete Todo',
'Are you sure you want to delete this todo?',
[
{ text: 'Cancel', style: 'cancel' },
{ text: 'Delete', onPress: () => setTodos(todos.filter(todo => todo.id !== id)) },
]
);
};
const renderTodo = ({ item }) => (
<View style={styles.todoItem}>
<TouchableOpacity
style={styles.todoText}
onPress={() => toggleTodo(item.id)}
>
<Text style={[
styles.todoTextContent,
item.completed && styles.completedText
]}>
{item.text}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.deleteButton}
onPress={() => deleteTodo(item.id)}
>
<Text style={styles.deleteButtonText}>Delete</Text>
</TouchableOpacity>
</View>
);
return (
<View style={styles.container}>
<Text style={styles.title}>Todo App</Text>
<View style={styles.inputContainer}>
<TextInput
style={styles.input}
value={inputText}
onChangeText={setInputText}
placeholder="Enter a new todo"
returnKeyType="done"
onSubmitEditing={addTodo}
/>
<TouchableOpacity style={styles.addButton} onPress={addTodo}>
<Text style={styles.addButtonText}>Add</Text>
</TouchableOpacity>
</View>
<FlatList
data={todos}
renderItem={renderTodo}
keyExtractor={(item) => item.id}
style={styles.todoList}
showsVerticalScrollIndicator={false}
/>
<Text style={styles.summary}>
Total: {todos.length} | Completed: {todos.filter(t => t.completed).length}
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
padding: 20,
},
title: {
fontSize: 28,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 30,
color: '#333',
},
inputContainer: {
flexDirection: 'row',
marginBottom: 20,
},
input: {
flex: 1,
borderWidth: 1,
borderColor: '#ddd',
padding: 12,
borderRadius: 8,
fontSize: 16,
backgroundColor: 'white',
},
addButton: {
backgroundColor: '#007bff',
paddingHorizontal: 20,
marginLeft: 10,
borderRadius: 8,
justifyContent: 'center',
},
addButtonText: {
color: 'white',
fontWeight: 'bold',
fontSize: 16,
},
todoList: {
flex: 1,
},
todoItem: {
flexDirection: 'row',
backgroundColor: 'white',
padding: 15,
marginVertical: 5,
borderRadius: 8,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 2,
},
todoText: {
flex: 1,
},
todoTextContent: {
fontSize: 16,
color: '#333',
},
completedText: {
textDecorationLine: 'line-through',
color: '#888',
},
deleteButton: {
backgroundColor: '#dc3545',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 4,
},
deleteButtonText: {
color: 'white',
fontSize: 12,
fontWeight: 'bold',
},
summary: {
textAlign: 'center',
marginTop: 20,
fontSize: 16,
color: '#666',
},
});
export default TodoApp;
Summary
React Native bridges the gap between web development and mobile app development, allowing React developers to leverage their existing skills to build native mobile applications. Key takeaways from this chapter include:
- Component Mapping: Understanding how web components translate to mobile components
- Styling Differences: Using StyleSheet instead of CSS
- Navigation: Implementing screen-to-screen navigation
- Platform-Specific Code: Handling iOS and Android differences
- Performance Considerations: Optimizing for mobile devices
- Development Tools: Using debugging and development tools effectively
React Native offers a powerful way to build cross-platform mobile applications while maintaining the familiar React development experience. As you continue your React Native journey, focus on understanding platform-specific design patterns and mobile user experience principles.
Next Steps
In the next chapter, we’ll explore deployment strategies for React applications, covering various hosting options and deployment pipelines for both web and mobile applications.