Skip to main content
React Native Core Components

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 ComponentReact NativeiOS NativeAndroid 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',
  },
});

Button Component

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>.

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>

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)}
/>

ScrollView Component

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',
  },
});

Horizontal ScrollView

<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>
  );
};

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',
  },
});

Platform-Specific Code

Using Platform.OS

import { Platform, StyleSheet } from 'react-native';

const styles = StyleSheet.create({
  container: {
    padding: 20,
    ...Platform.select({
      ios: {
        backgroundColor: '#f0f0f0',
      },
      android: {
        backgroundColor: '#fff',
      },
      default: {
        backgroundColor: '#fff',
      },
    }),
  },
});

Platform-Specific Files

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