Topic 6 of 15 · React Native

Topic 6 : Basics (styles,components, Text inputs, buttons, Scrollview, Activity indicator, images, Modals)

Lesson TL;DRTopic 6: Basics (Styles, Components, Text Inputs, Buttons, ScrollView, Activity Indicator, Images, Modals) 📖 10 min read · 🎯 intermediate · 🧭 Prerequisites: runningemulatorandsimulator, stateandpro...
10 min read·intermediate·react-native · styling · components · text-input

Topic 6: Basics (Styles, Components, Text Inputs, Buttons, ScrollView, Activity Indicator, Images, Modals)

📖 10 min read · 🎯 intermediate · 🧭 Prerequisites: running-emulator-and-simulator, state-and-props

Why this matters

You've already written your first React Native component — but right now it just sits there, static, doing nothing. Every real app you've used on your phone — whether it's scrolling through a feed, tapping a button, filling out a login form, or watching a spinner while something loads — is built from a handful of core building blocks. This lesson is where you meet all of them at once: styles, text inputs, buttons, scroll views, activity indicators, images, and modals. Learn these eight, and you'll recognise them everywhere you look.

What You'll Learn

  • Style components with StyleSheet.create using JavaScript objects instead of CSS strings
  • Build functional components for common UI patterns: text inputs, buttons, scroll views, activity indicators, images, and modals
  • Control component state to toggle visibility, capture input, and show/hide loading indicators
  • Compose all eight component types into a single working App.js

The Analogy

Think of building a React Native screen the same way a stage designer furnishes a theatre set. The StyleSheet is your paint and fabric — it determines colour, size, and spacing. Individual components (TextInput, Button, Image) are the furniture pieces you place on stage. ScrollView is the stage itself when it's too long to fit in one shot — you can scroll the curtain. And Modal is the spotlight that drops a scrim over everything else so the audience focuses on one critical moment. Put them all together and you have a scene your audience can actually interact with.

Chapter 1: Understanding Styles

Styling in React Native is similar to CSS but uses JavaScript objects defined through the StyleSheet module rather than string-based class names. Properties are camelCase (backgroundColor, fontSize, borderRadius) and most length values are unitless density-independent pixels.

StylesComponent.js

import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

const StylesComponent = () => {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Hello, Vizag!</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#f0f0f0',
  },
  text: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#333',
  },
});

export default StylesComponent;

Key points:

  • StyleSheet.create validates your style objects at development time and optimises them at runtime.
  • flex: 1 tells the container to fill available space along the main axis.
  • justifyContent controls alignment along the main axis; alignItems controls the cross axis.

Chapter 2: Creating Components

Components are the building blocks of a React Native application. Every screen you build is a tree of components — some from the RN core library, some you write yourself.

GreetingComponent.js

import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

const GreetingComponent = () => {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Welcome to Vizag!</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    padding: 20,
    backgroundColor: '#e0e0e0',
    borderRadius: 8,
  },
  text: {
    fontSize: 18,
    color: '#555',
  },
});

export default GreetingComponent;

A functional component is simply a JavaScript function that returns JSX. Keep components small and focused — each one should do one thing well.

Chapter 3: Using Text Inputs

TextInput lets users type data into your app. Pair it with useState to keep the current value in state and reflect it back in the UI.

TextInputComponent.js

import React, { useState } from 'react';
import { View, TextInput, Text, StyleSheet } from 'react-native';

const TextInputComponent = () => {
  const [text, setText] = useState('');

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.input}
        placeholder="Enter text"
        value={text}
        onChangeText={setText}
      />
      <Text style={styles.text}>You entered: {text}</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    padding: 20,
    backgroundColor: '#fff',
    borderRadius: 8,
  },
  input: {
    height: 40,
    borderColor: 'gray',
    borderWidth: 1,
    marginBottom: 10,
    paddingHorizontal: 8,
  },
  text: {
    fontSize: 16,
  },
});

export default TextInputComponent;

Notable props:

PropPurpose
valueControlled value — always reflects state
onChangeTextFires on every keystroke with the new string
placeholderGhost text shown when input is empty
paddingHorizontalKeeps typed text from hugging the border

Chapter 4: Adding Buttons

The Button component is the simplest way to add a tappable action. It renders a native button on each platform and fires onPress when tapped.

ButtonComponent.js

import React from 'react';
import { View, Button, Alert, StyleSheet } from 'react-native';

const ButtonComponent = () => {
  const showAlert = () => {
    Alert.alert('Button Pressed', 'You pressed the button!');
  };

  return (
    <View style={styles.container}>
      <Button title="Press Me" onPress={showAlert} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    padding: 20,
    backgroundColor: '#fff',
    borderRadius: 8,
  },
});

export default ButtonComponent;

Alert.alert(title, message) pops a native OS dialog — no extra libraries needed. For fully custom styling, swap Button for TouchableOpacity or Pressable and style it yourself.

Chapter 5: Using ScrollView

When content is taller than the screen, wrap it in ScrollView. Use contentContainerStyle (not style) to style the inner scrollable area.

ScrollViewComponent.js

import React from 'react';
import { ScrollView, View, Text, StyleSheet } from 'react-native';

const ScrollViewComponent = () => {
  return (
    <ScrollView contentContainerStyle={styles.container}>
      {Array.from({ length: 20 }).map((_, index) => (
        <View key={index} style={styles.item}>
          <Text>Item {index + 1}</Text>
        </View>
      ))}
    </ScrollView>
  );
};

const styles = StyleSheet.create({
  container: {
    padding: 20,
  },
  item: {
    padding: 20,
    marginBottom: 10,
    backgroundColor: '#f9f9f9',
    borderRadius: 8,
  },
});

export default ScrollViewComponent;

Tip: ScrollView renders all children at once. For very long lists (hundreds of items), prefer FlatList or SectionList which virtualise off-screen rows.

Chapter 6: Showing an Activity Indicator

ActivityIndicator is the standard spinning loader. Combine it with useEffect and a timeout (or a real data-fetch) to show it while content loads, then swap it out once loading is done.

ActivityIndicatorComponent.js

import React, { useState, useEffect } from 'react';
import { View, ActivityIndicator, Text, StyleSheet } from 'react-native';

const ActivityIndicatorComponent = () => {
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setTimeout(() => {
      setLoading(false);
    }, 3000);
  }, []);

  return (
    <View style={styles.container}>
      {loading ? (
        <ActivityIndicator size="large" color="#0000ff" />
      ) : (
        <Text>Data loaded!</Text>
      )}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

export default ActivityIndicatorComponent;

Key props:

  • size"small" or "large" (or a number on Android)
  • color — any valid colour string; here "#0000ff" for blue

The useEffect with an empty dependency array [] runs once after mount — perfect for kicking off a data load.

Chapter 7: Displaying Images

Use the core Image component with a source prop. For remote URLs, pass { uri: '...' }. Always specify explicit width and height in the style — without them the image won't render.

ImageComponent.js

import React from 'react';
import { View, Image, StyleSheet } from 'react-native';

const ImageComponent = () => {
  return (
    <View style={styles.container}>
      <Image
        style={styles.image}
        source={{ uri: 'https://your-image-url.com/image.jpg' }}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  image: {
    width: 200,
    height: 200,
  },
});

export default ImageComponent;

For local assets use source={require('./assets/photo.jpg')}. For advanced caching and performance on image-heavy screens, the community library react-native-fast-image is the go-to drop-in replacement.

Chapter 8: Using Modals

Modal renders an overlay above all other content. Control its visibility with a boolean in state, wire the open/close to buttons, and always implement onRequestClose (required on Android back-button press).

ModalComponent.js

import React, { useState } from 'react';
import { View, Text, Button, Modal, StyleSheet } from 'react-native';

const ModalComponent = () => {
  const [modalVisible, setModalVisible] = useState(false);

  return (
    <View style={styles.container}>
      <Button title="Show Modal" onPress={() => setModalVisible(true)} />
      <Modal
        animationType="slide"
        transparent={true}
        visible={modalVisible}
        onRequestClose={() => {
          setModalVisible(!modalVisible);
        }}
      >
        <View style={styles.modalContainer}>
          <View style={styles.modalContent}>
            <Text style={styles.modalText}>Hello, I am a modal!</Text>
            <Button title="Hide Modal" onPress={() => setModalVisible(false)} />
          </View>
        </View>
      </Modal>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  modalContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'rgba(0, 0, 0, 0.5)',
  },
  modalContent: {
    width: 300,
    padding: 20,
    backgroundColor: '#fff',
    borderRadius: 8,
    alignItems: 'center',
  },
  modalText: {
    fontSize: 18,
    marginBottom: 20,
  },
});

export default ModalComponent;

animationType accepts "none", "slide", or "fade". The semi-transparent black backgroundColor: 'rgba(0, 0, 0, 0.5)' on the outer container creates the classic dimmed-backdrop effect.

Chapter 9: Combining All Components in App.js

The class's final step: import all eight components into App.js and render them together inside a top-level ScrollView.

App.js

import React from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import StylesComponent from './StylesComponent';
import GreetingComponent from './GreetingComponent';
import TextInputComponent from './TextInputComponent';
import ButtonComponent from './ButtonComponent';
import ScrollViewComponent from './ScrollViewComponent';
import ActivityIndicatorComponent from './ActivityIndicatorComponent';
import ImageComponent from './ImageComponent';
import ModalComponent from './ModalComponent';

export default function App() {
  return (
    <ScrollView contentContainerStyle={styles.container}>
      <StylesComponent />
      <GreetingComponent />
      <TextInputComponent />
      <ButtonComponent />
      <ScrollViewComponent />
      <ActivityIndicatorComponent />
      <ImageComponent />
      <ModalComponent />
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flexGrow: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 16,
  },
});

flexGrow: 1 on contentContainerStyle ensures the inner container expands to fill the screen even when content is short, keeping everything centred. Note the outer ScrollView wraps a nested ScrollViewComponent — this works here for demo purposes, but in production avoid deeply nested scroll views on the same axis.

graph TD
  App["App.js (ScrollView)"]
  App --> SC[StylesComponent]
  App --> GC[GreetingComponent]
  App --> TI[TextInputComponent]
  App --> BC[ButtonComponent]
  App --> SV[ScrollViewComponent]
  App --> AI[ActivityIndicatorComponent]
  App --> IC[ImageComponent]
  App --> MC[ModalComponent]

🧪 Try It Yourself

Task: Build a mini profile card screen that combines three of the eight primitives.

  1. Create ProfileCard.js with:
    • An Image showing a placeholder avatar (use https://via.placeholder.com/100 as the URI)
    • A TextInput for the user to type their display name
    • A Button labelled "Save Name" that fires Alert.alert('Saved!', name) where name is the current input value
  2. Import and render <ProfileCard /> inside App.js.

Success criterion: You should see the avatar image, be able to type a name, tap "Save Name", and see a native alert that echoes back exactly what you typed.

Starter snippet:

import React, { useState } from 'react';
import { View, Image, TextInput, Button, Alert, StyleSheet } from 'react-native';

const ProfileCard = () => {
  const [name, setName] = useState('');

  return (
    <View style={styles.card}>
      <Image
        style={styles.avatar}
        source={{ uri: 'https://via.placeholder.com/100' }}
      />
      <TextInput
        style={styles.input}
        placeholder="Your display name"
        value={name}
        onChangeText={setName}
      />
      <Button
        title="Save Name"
        onPress={() => Alert.alert('Saved!', name)}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  card: { padding: 20, alignItems: 'center', backgroundColor: '#fff', borderRadius: 12 },
  avatar: { width: 100, height: 100, borderRadius: 50, marginBottom: 12 },
  input: { width: '100%', height: 40, borderColor: '#ccc', borderWidth: 1, paddingHorizontal: 8, marginBottom: 12, borderRadius: 6 },
});

export default ProfileCard;

🔍 Checkpoint Quiz

Q1. Why does React Native use StyleSheet.create instead of plain inline objects?

A) It automatically converts styles to CSS
B) It validates styles at dev time and optimises them at runtime
C) It is required — inline objects crash the app
D) It enables media queries on mobile

Q2. Given this snippet, what renders below the TextInput after typing "hello"?

const [text, setText] = useState('');
// ...
<TextInput value={text} onChangeText={setText} />
<Text>You entered: {text}</Text>

A) You entered:
B) You entered: undefined
C) You entered: hello
D) Nothing — Text requires a separate state variable

Q3. What is the bug in this ActivityIndicator usage?

const [loading, setLoading] = useState(true);
// useEffect omitted
return <ActivityIndicator visible={loading} size="large" color="#f00" />;

A) color must be a hex number, not a string
B) ActivityIndicator has no visible prop — use a conditional render instead
C) size="large" is not valid; use size={2}
D) There is no bug

Q4. You need to display 500 items from an API in a scrollable list. Which component should you choose and why?

A1. B — StyleSheet.create validates style keys and values in development and serialises the object to a native ID at runtime, reducing bridge overhead compared to recreating plain objects on every render.

A2. C — onChangeText={setText} updates text state on every keystroke, and value={text} makes the input controlled, so the Text below always mirrors the current state.

A3. B — ActivityIndicator does not accept a visible prop. The correct pattern is to conditionally render the component: {loading && <ActivityIndicator size="large" color="#f00" />} or use a ternary as shown in the lesson.

A4. Use FlatList (or SectionList if grouped). ScrollView renders all 500 children immediately, bloating memory and causing jank. FlatList virtualises off-screen rows, only keeping a window of DOM nodes in memory at once — critical for large datasets.

🪞 Recap

  • StyleSheet.create takes JavaScript camelCase objects — no CSS strings — and optimises them for the native bridge.
  • TextInput must be controlled: bind value to state and update it with onChangeText.
  • Button triggers onPress; pair with Alert.alert for quick native feedback without extra libraries.
  • ScrollView is ideal for short, static content; use FlatList for long or dynamic lists.
  • ActivityIndicator is shown/hidden via conditional rendering driven by a loading state variable.
  • Image requires explicit width and height styles; remote images use source={{ uri: '...' }}.
  • Modal needs onRequestClose for Android back-button support and uses transparent + rgba background for the dimmed overlay effect.

📚 Further Reading

Like this topic? It’s one of 15 in React Native.

Block your seat for ₹2,500 and join the next cohort.