All Posts
react-nativePart 2 of react-native-expo

React Native #2 — Core Components

Every component you'll use in 90% of your screens: View, Text, Image, ScrollView, FlatList, TextInput, and Pressable. With real examples and gotchas.

R
by Rupa
Apr 4, 20264 min read

The Building Blocks

ComponentsDemo.tsx
Loading editor...

View — The div of React Native

View is a layout container. It supports flexbox, styling, and touch handling. Most screens are just nested Views:

<View style={{ flex: 1, padding: 20 }}>
  <View style={{ backgroundColor: '#1a1a1a', borderRadius: 12, padding: 16 }}>
    <Text>Card content</Text>
  </View>
</View>

Text — The Only Way to Render Strings

<Text
  style={{ fontSize: 16, color: '#fff' }}
  numberOfLines={2}          // truncate after 2 lines
  ellipsizeMode="tail"       // "...", "head", "middle", "clip"
  selectable={true}          // allow long-press to copy
  onPress={() => alert('tapped')}
>
  This is text content
</Text>

{/* Nested Text for mixed styles */}
<Text style={{ color: '#fff', fontSize: 16 }}>
  Hello{' '}
  <Text style={{ fontWeight: 'bold', color: '#6366f1' }}>React Native</Text>
  {' '}world!
</Text>
Strings must be inside Text
// ❌ Crashes — raw string in View
<View>{isLoggedIn && "Welcome back!"}</View>

// ✅ Wrap in Text
<View>{isLoggedIn && <Text>Welcome back!</Text>}</View>

Image

import { Image } from 'react-native';

// Remote image — must specify width and height
<Image
  source={{ uri: 'https://picsum.photos/300/200' }}
  style={{ width: 300, height: 200, borderRadius: 12 }}
  resizeMode="cover"   // cover | contain | stretch | center
/>

// Local image (in assets folder)
<Image
  source={require('../assets/images/logo.png')}
  style={{ width: 80, height: 80 }}
/>

For better performance, use expo-image:

npx expo install expo-image
import { Image } from 'expo-image';

<Image
  source="https://picsum.photos/300/200"
  style={{ width: 300, height: 200, borderRadius: 12 }}
  contentFit="cover"
  placeholder={blurhash}   // shows a blur while loading
  transition={200}
/>

ScrollView vs FlatList

ScrollView — renders all children at once. Use for short, static content:

<ScrollView
  contentContainerStyle={{ padding: 20, gap: 12 }}
  showsVerticalScrollIndicator={false}
  keyboardShouldPersistTaps="handled"   // taps work when keyboard is open
>
  <Card />
  <Card />
  <Card />
</ScrollView>

FlatList — virtualised list. Only renders visible items. Use this for any dynamic list:

<FlatList
  data={products}
  keyExtractor={item => String(item.id)}
  renderItem={({ item, index }) => <ProductCard product={item} />}
  contentContainerStyle={{ padding: 16, gap: 12 }}

  // Performance
  removeClippedSubviews={true}
  initialNumToRender={10}
  maxToRenderPerBatch={10}

  // Empty state
  ListEmptyComponent={<Text style={{ color: '#888' }}>No items found</Text>}

  // Header and footer
  ListHeaderComponent={<Text style={{ color: '#fff' }}>All Products</Text>}
  ListFooterComponent={isLoadingMore ? <ActivityIndicator /> : null}

  // Pull to refresh
  refreshControl={
    <RefreshControl
      refreshing={refreshing}
      onRefresh={handleRefresh}
      tintColor="#6366f1"
    />
  }

  // Pagination
  onEndReached={loadMoreItems}
  onEndReachedThreshold={0.3}   // trigger when 30% from bottom
/>
Never put FlatList inside ScrollView

Nesting a FlatList inside a ScrollView breaks virtualisation — all items render at once and you get scroll conflicts. Use FlatList's ListHeaderComponent and ListFooterComponent instead.

SectionList — Grouped Lists

import { SectionList } from 'react-native';

const sections = [
  { title: 'Electronics', data: ['Laptop', 'Phone', 'Tablet'] },
  { title: 'Clothing',    data: ['T-Shirt', 'Jeans'] },
];

<SectionList
  sections={sections}
  keyExtractor={(item, index) => item + index}
  renderItem={({ item }) => (
    <Text style={{ color: '#fff', padding: 12 }}>{item}</Text>
  )}
  renderSectionHeader={({ section }) => (
    <Text style={{ color: '#6366f1', fontWeight: '700', padding: 12,
                   backgroundColor: '#0f0f0f' }}>
      {section.title}
    </Text>
  )}
/>

TextInput

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

<TextInput
  value={value}
  onChangeText={setValue}
  placeholder="Search..."
  placeholderTextColor="#666"
  style={styles.input}

  // Keyboard type
  keyboardType="email-address"  // "numeric" | "phone-pad" | "decimal-pad"
  returnKeyType="search"        // "done" | "next" | "go" | "send"
  autoCapitalize="none"         // "words" | "sentences" | "characters"
  autoCorrect={false}
  secureTextEntry={true}        // for passwords

  // Refs for focus management
  ref={inputRef}
  onSubmitEditing={() => nextInputRef.current?.focus()}
  blurOnSubmit={false}          // don't blur when pressing return (for multi-input forms)
/>

Pressable — The Modern Touch Handler

Prefer Pressable over TouchableOpacity for new code:

<Pressable
  onPress={handlePress}
  onLongPress={handleLongPress}
  style={({ pressed }) => [
    styles.button,
    pressed && { opacity: 0.75, transform: [{ scale: 0.97 }] }
  ]}
  hitSlop={8}   // extend touch area by 8dp on all sides
>
  {({ pressed }) => (
    <Text style={{ color: pressed ? '#aaa' : '#fff' }}>Press me</Text>
  )}
</Pressable>

KeyboardAvoidingView — Forms That Don't Get Covered

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

<KeyboardAvoidingView
  style={{ flex: 1 }}
  behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
  keyboardVerticalOffset={64}   // account for header height
>
  <ScrollView>
    <TextInput placeholder="Email" />
    <TextInput placeholder="Password" secureTextEntry />
    <Button title="Login" />
  </ScrollView>
</KeyboardAvoidingView>

What's Next?

React Native #3 covers StyleSheet and Flexbox in depth — how React Native's layout system works, responsive sizing with Dimensions, and theming with a design system.

#react-native#expo#components#flatlist

✦ Enjoyed this post?

Get posts like this in your inbox

No spam, just real tutorials when they're ready.

Discussion

Powered by GitHub

Comments use GitHub Discussions — no separate account needed if you have GitHub.