Core Components
React Native provides a set of ready-to-use native components that translate to platform-specific equivalents. Instead of <div>, <span>, and <img>, you’ll use <View>, <Text>, and <Image>.
Component Mapping
Web Component React Native iOS Native Android Native <div><View>UIViewandroid.view.View<span>, <p><Text>UILabelTextView<img><Image>UIImageViewImageView<input><TextInput>UITextFieldEditText<button><Button>, <Pressable>UIButtonButton<ul>, <ol><FlatList>, <SectionList>UITableViewRecyclerView
Key Concept : React Native components compile to native platform components , not web views. This is why apps feel native!
View Component
<View> is the most fundamental component—it’s a container that supports layout, styling, touch handling, and accessibility.
import { View , StyleSheet } from 'react-native' ;
const App = () => {
return (
< View style = { styles . container } >
< View style = { styles . box } >
{ /* Nested views */ }
</ View >
</ View >
);
};
const styles = StyleSheet . create ({
container: {
flex: 1 ,
backgroundColor: '#f5f5f5' ,
},
box: {
width: 100 ,
height: 100 ,
backgroundColor: '#007AFF' ,
},
});
View Props
< View
// Styling
style = { styles . container }
// Touch handling
onTouchStart = { ( e ) => console . log ( 'Touch started' ) }
onTouchEnd = { ( e ) => console . log ( 'Touch ended' ) }
// Accessibility
accessible = { true }
accessibilityLabel = "Main container"
// Pointer events
pointerEvents = "box-none" // 'box-none' | 'none' | 'box-only' | 'auto'
>
{ /* Children */ }
</ View >
SafeAreaView
<SafeAreaView> renders content within the safe area boundaries of a device (avoiding notches, home indicators, etc.).
import { SafeAreaView , Text , StyleSheet } from 'react-native' ;
const App = () => {
return (
< SafeAreaView style = { styles . container } >
< Text > This text avoids the notch! </ Text >
</ SafeAreaView >
);
};
const styles = StyleSheet . create ({
container: {
flex: 1 ,
backgroundColor: '#fff' ,
},
});
iOS Only : SafeAreaView only works on iOS 11+. For Android, use react-native-safe-area-context library for better cross-platform support.
Better Alternative: react-native-safe-area-context
npm install react-native-safe-area-context
import { SafeAreaProvider , SafeAreaView } from 'react-native-safe-area-context' ;
const App = () => {
return (
< SafeAreaProvider >
< SafeAreaView style = { { flex: 1 } } >
< Text > Works on iOS and Android! </ Text >
</ SafeAreaView >
</ SafeAreaProvider >
);
};
Text Component
All text must be wrapped in a <Text> component. Unlike the web, you cannot put text directly in a <View>.
import { Text , StyleSheet } from 'react-native' ;
const App = () => {
return (
<>
< Text style = { styles . title } > This is a title </ Text >
< Text style = { styles . body } > This is body text </ Text >
{ /* Nested text inherits parent styles */ }
< Text style = { styles . bold } >
This is < Text style = { styles . italic } > bold with italic </ Text >
</ Text >
</>
);
};
const styles = StyleSheet . create ({
title: {
fontSize: 24 ,
fontWeight: 'bold' ,
color: '#000' ,
},
body: {
fontSize: 16 ,
color: '#666' ,
},
bold: {
fontWeight: 'bold' ,
},
italic: {
fontStyle: 'italic' ,
},
});
Text Props
< Text
// Styling
style = { styles . text }
// Number of lines
numberOfLines = { 2 }
// Ellipsis mode
ellipsizeMode = "tail" // 'head' | 'middle' | 'tail' | 'clip'
// Selection
selectable = { true }
// Press handling
onPress = { () => console . log ( 'Text pressed' ) }
onLongPress = { () => console . log ( 'Long press' ) }
// Accessibility
accessibilityRole = "header"
>
Press me!
</ Text >
Text Truncation
const LongText = () => {
const longText = "This is a very long text that will be truncated to fit within the container..." ;
return (
< View style = { { width: 200 } } >
< Text numberOfLines = { 2 } ellipsizeMode = "tail" >
{ longText }
</ Text >
</ View >
);
};
Image Component
Display images from various sources: local files, network URLs, or base64 data.
Local Images
import { Image , StyleSheet } from 'react-native' ;
const App = () => {
return (
< Image
source = { require ( './assets/logo.png' ) }
style = { styles . image }
/>
);
};
const styles = StyleSheet . create ({
image: {
width: 100 ,
height: 100 ,
},
});
Important : Local images require explicit width and height. React Native cannot infer dimensions from require().
Network Images
const App = () => {
return (
< Image
source = { { uri: 'https://example.com/image.png' } }
style = { { width: 200 , height: 200 } }
/>
);
};
Image with Loading States
import { useState } from 'react' ;
import { Image , ActivityIndicator , View } from 'react-native' ;
const NetworkImage = ({ uri }) => {
const [ loading , setLoading ] = useState ( true );
return (
< View style = { { width: 200 , height: 200 } } >
{ loading && (
< ActivityIndicator
style = { { position: 'absolute' , alignSelf: 'center' , top: '50%' } }
/>
) }
< Image
source = { { uri } }
style = { { width: '100%' , height: '100%' } }
onLoadStart = { () => setLoading ( true ) }
onLoadEnd = { () => setLoading ( false ) }
onError = { () => setLoading ( false ) }
/>
</ View >
);
};
Image Props
< Image
source = { { uri: 'https://...' } }
style = { { width: 200 , height: 200 } }
// Resize mode
resizeMode = "cover" // 'cover' | 'contain' | 'stretch' | 'repeat' | 'center'
// Loading events
onLoadStart = { () => console . log ( 'Loading started' ) }
onLoad = { ( event ) => console . log ( 'Image loaded' , event . nativeEvent ) }
onLoadEnd = { () => console . log ( 'Loading ended' ) }
onError = { ( error ) => console . error ( 'Error loading image' , error ) }
// Progress (iOS only)
onProgress = { ( event ) => {
const { loaded , total } = event . nativeEvent ;
console . log ( `Progress: ${ ( loaded / total ) * 100 } %` );
} }
/>
ImageBackground
For images as backgrounds:
import { ImageBackground , Text , StyleSheet } from 'react-native' ;
const App = () => {
return (
< ImageBackground
source = { require ( './assets/background.png' ) }
style = { styles . background }
resizeMode = "cover"
>
< Text style = { styles . text } > Text over image </ Text >
</ ImageBackground >
);
};
const styles = StyleSheet . create ({
background: {
flex: 1 ,
justifyContent: 'center' ,
alignItems: 'center' ,
},
text: {
color: 'white' ,
fontSize: 24 ,
fontWeight: 'bold' ,
},
});
Simple, platform-specific button.
import { Button , Alert } from 'react-native' ;
const App = () => {
return (
< Button
title = "Press Me"
onPress = { () => Alert . alert ( 'Button pressed!' ) }
color = "#007AFF" // iOS: text color, Android: background color
disabled = { false }
/>
);
};
Limited Customization : The built-in <Button> component has limited styling options. For more control, use <Pressable> or <TouchableOpacity>.
Pressable Component (Recommended)
Modern, flexible pressable component with advanced press handling.
import { Pressable , Text , StyleSheet } from 'react-native' ;
const CustomButton = () => {
return (
< Pressable
style = { ({ pressed }) => [
styles . button ,
pressed && styles . pressed ,
] }
onPress = { () => console . log ( 'Pressed' ) }
onLongPress = { () => console . log ( 'Long pressed' ) }
onPressIn = { () => console . log ( 'Press in' ) }
onPressOut = { () => console . log ( 'Press out' ) }
hitSlop = { 10 } // Increase touch area
pressRetentionOffset = { 10 } // Keep active when finger moves
>
{ ({ pressed }) => (
< Text style = { styles . text } >
{ pressed ? 'Pressed!' : 'Press me' }
</ Text >
) }
</ Pressable >
);
};
const styles = StyleSheet . create ({
button: {
padding: 15 ,
backgroundColor: '#007AFF' ,
borderRadius: 8 ,
alignItems: 'center' ,
},
pressed: {
backgroundColor: '#0051D5' ,
},
text: {
color: '#fff' ,
fontSize: 16 ,
fontWeight: '600' ,
},
});
Pressable with Ripple Effect (Android)
< Pressable
android_ripple = { {
color: 'rgba(0, 0, 0, 0.1)' ,
borderless: false ,
} }
style = { styles . button }
>
< Text > Android Ripple </ Text >
</ Pressable >
TouchableOpacity (Legacy, Still Popular)
Fades the view when pressed.
import { TouchableOpacity , Text , StyleSheet } from 'react-native' ;
const App = () => {
return (
< TouchableOpacity
style = { styles . button }
onPress = { () => console . log ( 'Pressed' ) }
activeOpacity = { 0.7 }
>
< Text style = { styles . text } > Press Me </ Text >
</ TouchableOpacity >
);
};
const styles = StyleSheet . create ({
button: {
padding: 15 ,
backgroundColor: '#007AFF' ,
borderRadius: 8 ,
alignItems: 'center' ,
},
text: {
color: '#fff' ,
fontSize: 16 ,
fontWeight: '600' ,
},
});
TextInput Component
For user text input.
import { useState } from 'react' ;
import { TextInput , View , Text , StyleSheet } from 'react-native' ;
const App = () => {
const [ text , setText ] = useState ( '' );
return (
< View style = { styles . container } >
< TextInput
style = { styles . input }
value = { text }
onChangeText = { setText }
placeholder = "Enter text"
placeholderTextColor = "#999"
/>
< Text > You typed: { text } </ Text >
</ View >
);
};
const styles = StyleSheet . create ({
container: {
padding: 20 ,
},
input: {
height: 50 ,
borderWidth: 1 ,
borderColor: '#ddd' ,
borderRadius: 8 ,
paddingHorizontal: 15 ,
fontSize: 16 ,
marginBottom: 10 ,
},
});
TextInput Types
// Email
< TextInput
keyboardType = "email-address"
autoCapitalize = "none"
autoCorrect = { false }
textContentType = "emailAddress" // iOS autocomplete
/>
// Password
< TextInput
secureTextEntry = { true }
textContentType = "password"
/>
// Phone number
< TextInput
keyboardType = "phone-pad"
textContentType = "telephoneNumber"
/>
// Number
< TextInput
keyboardType = "numeric"
/>
// Multiline
< TextInput
multiline = { true }
numberOfLines = { 4 }
style = { { height: 100 , textAlignVertical: 'top' } }
/>
TextInput Props
< TextInput
// Value
value = { text }
onChangeText = { setText }
// Placeholder
placeholder = "Enter text"
placeholderTextColor = "#999"
// Keyboard
keyboardType = "default" // 'numeric' | 'email-address' | 'phone-pad' etc
returnKeyType = "done" // 'done' | 'go' | 'next' | 'search' | 'send'
// Capitalization
autoCapitalize = "sentences" // 'none' | 'sentences' | 'words' | 'characters'
// Autocorrect
autoCorrect = { true }
// Security
secureTextEntry = { false }
// Multiline
multiline = { false }
numberOfLines = { 1 }
// Focus
autoFocus = { false }
onFocus = { () => console . log ( 'Focused' ) }
onBlur = { () => console . log ( 'Blurred' ) }
// Submit
onSubmitEditing = { () => console . log ( 'Submitted' ) }
// Selection
selection = { { start: 0 , end: 0 } }
onSelectionChange = { ( e ) => console . log ( e . nativeEvent . selection ) }
/>
For scrollable content.
import { ScrollView , Text , View , StyleSheet } from 'react-native' ;
const App = () => {
return (
< ScrollView
style = { styles . container }
showsVerticalScrollIndicator = { true }
>
{ [ ... Array ( 20 )]. map (( _ , i ) => (
< View key = { i } style = { styles . item } >
< Text > Item { i + 1 } </ Text >
</ View >
)) }
</ ScrollView >
);
};
const styles = StyleSheet . create ({
container: {
flex: 1 ,
},
item: {
padding: 20 ,
borderBottomWidth: 1 ,
borderBottomColor: '#ddd' ,
},
});
< ScrollView
horizontal = { true }
showsHorizontalScrollIndicator = { false }
pagingEnabled = { true } // Snap to pages
>
{ images . map (( img , i ) => (
< Image key = { i } source = { img } style = { { width: 300 , height: 200 } } />
)) }
</ ScrollView >
Performance : Don’t use ScrollView for long lists! Use FlatList instead (covered in Module 6).
ActivityIndicator
Loading spinner.
import { ActivityIndicator , View } from 'react-native' ;
const App = () => {
return (
< View style = { { flex: 1 , justifyContent: 'center' , alignItems: 'center' } } >
< ActivityIndicator size = "large" color = "#007AFF" />
</ View >
);
};
Switch Component
Toggle switch.
import { useState } from 'react' ;
import { Switch , View , Text } from 'react-native' ;
const App = () => {
const [ isEnabled , setIsEnabled ] = useState ( false );
return (
< View style = { { flexDirection: 'row' , alignItems: 'center' , padding: 20 } } >
< Text style = { { flex: 1 } } > Enable notifications </ Text >
< Switch
value = { isEnabled }
onValueChange = { setIsEnabled }
trackColor = { { false: '#767577' , true: '#81b0ff' } }
thumbColor = { isEnabled ? '#007AFF' : '#f4f3f4' }
/>
</ View >
);
};
Modal Component
Overlay content.
import { useState } from 'react' ;
import { Modal , View , Text , Button , StyleSheet } from 'react-native' ;
const App = () => {
const [ visible , setVisible ] = useState ( false );
return (
< View style = { styles . container } >
< Button title = "Show Modal" onPress = { () => setVisible ( true ) } />
< Modal
visible = { visible }
animationType = "slide" // 'none' | 'slide' | 'fade'
transparent = { true }
onRequestClose = { () => setVisible ( false ) }
>
< View style = { styles . modalOverlay } >
< View style = { styles . modalContent } >
< Text style = { styles . modalText } > This is a modal! </ Text >
< Button title = "Close" onPress = { () => setVisible ( false ) } />
</ View >
</ View >
</ Modal >
</ View >
);
};
const styles = StyleSheet . create ({
container: {
flex: 1 ,
justifyContent: 'center' ,
alignItems: 'center' ,
},
modalOverlay: {
flex: 1 ,
backgroundColor: 'rgba(0, 0, 0, 0.5)' ,
justifyContent: 'center' ,
alignItems: 'center' ,
},
modalContent: {
backgroundColor: '#fff' ,
padding: 20 ,
borderRadius: 10 ,
width: '80%' ,
},
modalText: {
fontSize: 18 ,
marginBottom: 15 ,
textAlign: 'center' ,
},
});
import { Platform , StyleSheet } from 'react-native' ;
const styles = StyleSheet . create ({
container: {
padding: 20 ,
... Platform . select ({
ios: {
backgroundColor: '#f0f0f0' ,
},
android: {
backgroundColor: '#fff' ,
},
default: {
backgroundColor: '#fff' ,
},
}),
},
});
MyComponent.ios.tsx // iOS version
MyComponent.android.tsx // Android version
React Native automatically picks the right file!
// MyComponent.ios.tsx
export const MyComponent = () => < Text > iOS Version </ Text > ;
// MyComponent.android.tsx
export const MyComponent = () => < Text > Android Version </ Text > ;
// App.tsx
import { MyComponent } from './MyComponent' ; // Auto-selects!
Hands-On: Product Card Component
Let’s build a reusable product card:
import React from 'react' ;
import {
View ,
Text ,
Image ,
TouchableOpacity ,
StyleSheet ,
Platform ,
} from 'react-native' ;
interface ProductCardProps {
title : string ;
price : number ;
image : string ;
onPress : () => void ;
}
export const ProductCard : React . FC < ProductCardProps > = ({
title ,
price ,
image ,
onPress ,
}) => {
return (
< TouchableOpacity style = { styles . card } onPress = { onPress } activeOpacity = { 0.8 } >
< Image source = { { uri: image } } style = { styles . image } />
< View style = { styles . info } >
< Text style = { styles . title } numberOfLines = { 2 } >
{ title }
</ Text >
< Text style = { styles . price } > $ { price . toFixed ( 2 ) } </ Text >
</ View >
</ TouchableOpacity >
);
};
const styles = StyleSheet . create ({
card: {
backgroundColor: '#fff' ,
borderRadius: 12 ,
overflow: 'hidden' ,
marginBottom: 15 ,
... Platform . select ({
ios: {
shadowColor: '#000' ,
shadowOffset: { width: 0 , height: 2 },
shadowOpacity: 0.1 ,
shadowRadius: 4 ,
},
android: {
elevation: 3 ,
},
}),
},
image: {
width: '100%' ,
height: 200 ,
backgroundColor: '#f0f0f0' ,
},
info: {
padding: 12 ,
},
title: {
fontSize: 16 ,
fontWeight: '600' ,
color: '#333' ,
marginBottom: 8 ,
},
price: {
fontSize: 18 ,
fontWeight: 'bold' ,
color: '#007AFF' ,
},
});
// Usage
const App = () => {
return (
< ScrollView style = { { flex: 1 , padding: 15 } } >
< ProductCard
title = "Wireless Headphones"
price = { 99.99 }
image = "https://example.com/headphones.jpg"
onPress = { () => console . log ( 'Product pressed' ) }
/>
</ ScrollView >
);
};
Summary
In this module, you learned:
✅ Core React Native components and their native equivalents
✅ View, Text, Image, Button, and Pressable
✅ TextInput for user input with various keyboard types
✅ ScrollView for scrollable content
✅ Platform-specific code and styling
✅ Building reusable component (ProductCard)
Next up : Learn Flexbox layout and styling to build beautiful UIs!
Next: Styling & Layout Master Flexbox and create responsive designs