Topic 47 of 56 · Full Stack Advanced

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: stateandprops Why this matters Up until ...
10 min read·intermediate·react-native · mobile · components · styling

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

📖 10 min read · 🎯 intermediate · 🧭 Prerequisites: state-and-props

Why this matters

Up until now, you've probably seen mobile apps and wondered — how do they make that loading spinner, that scrollable list, that pop-up dialog? Here's the thing — every screen you've ever used on your phone is built from a small set of repeating pieces. React Native gives you exactly eight of those pieces to start with: StyleSheet, TextInput, Button, ScrollView, ActivityIndicator, Image, and Modal. Once you understand these, you're not just learning syntax — you're learning the vocabulary of every mobile app. Let's go through each one.

What You'll Learn

  • Define and apply styles with React Native's StyleSheet API using JavaScript objects
  • Build functional components for common UI patterns: text input, button, scrollable list, and modal overlay
  • Control loading state visually with ActivityIndicator and render remote images with Image
  • Combine all eight component types into a single cohesive App.js

The Analogy

Think of building a React Native screen like furnishing a new apartment. StyleSheet is the interior design rulebook — it dictates colours, spacing, and fonts. Each component (TextInput, Button, Modal) is a piece of furniture with a specific job. ScrollView is the floor plan that lets you keep adding furniture past the visible edge of the room. ActivityIndicator is the "please wait" sign on a contractor's door. And Image is the artwork you hang on the wall once everything else is in place.

Chapter 1: Understanding Styles

Styling in React Native resembles CSS but uses JavaScript objects rather than stylesheets. The StyleSheet.create() method validates those objects at compile time and optimises them for the native bridge.

Key properties:

  • flex / flexDirection / justifyContent / alignItems — Flexbox layout (default axis is column, unlike the web)
  • backgroundColor, color — Background and text colour using hex strings or named colours
  • fontSize, fontWeight — Typography control
  • padding, paddingHorizontal, margin, borderRadius — Spacing and shape

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;

Chapter 2: Creating Components

Components are the building blocks of a React Native application — every screen, card, or widget is a component. A functional component is a plain JavaScript function that returns JSX.

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;

Keeping components small and focused makes them easy to reuse anywhere in the app.

Chapter 3: Using Text Inputs

TextInput is the core component for capturing keyboard input. It is a controlled component — you store the value in state and pass it back via the value prop, keeping the UI and data in sync.

Key props:

PropPurpose
valueCurrent controlled value
onChangeTextCallback fired on every keystroke, receives the new string
placeholderGrey hint text shown when empty
height / borderColor / borderWidthStyle props for the input box

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;

Chapter 4: Adding Buttons

React Native's built-in Button renders a native-platform button (blue on iOS, teal/accent on Android). For taps, pass an onPress handler. Here the handler uses Alert.alert() to fire a native dialog.

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) is a cross-platform native dialog — no extra libraries required.

Chapter 5: Using ScrollView

ScrollView renders all its children at once and wraps them in a scrollable container. It is perfect for short lists and mixed content. Use contentContainerStyle (not style) to control the inner layout.

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;

Heads-up: For very long lists (hundreds of items) prefer FlatList or SectionList — they virtualise the DOM and only render visible rows. ScrollView mounts every child eagerly.

Chapter 6: Showing Activity Indicator

ActivityIndicator renders the platform's native spinner. It is most useful paired with a loading boolean in state — show the spinner while an async operation runs, then replace it with real content.

This example uses useEffect + setTimeout to simulate a 3-second network request.

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;

size accepts "small" or "large". color accepts any CSS-style colour string.

Chapter 7: Displaying Images

The Image component renders both local assets and remote URLs. Remote images must have explicit width and height in the style — React Native cannot infer dimensions from the network at layout time.

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 files use source={require('./assets/photo.jpg')} instead of a URI object. For advanced caching and performance on long image lists, consider the community library react-native-fast-image.

Chapter 8: Using Modals

Modal renders content in an overlay that floats above the rest of the UI. State controls visibility; animationType controls the entry animation.

Key props:

PropValuesPurpose
visiblebooleanShow or hide the modal
animationType"none", "slide", "fade"Entry/exit animation
transparentbooleanLets background show through
onRequestClosefunctionAndroid back-button handler (required on Android)

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;

The semi-transparent rgba(0, 0, 0, 0.5) backdrop on modalContainer creates the classic dimmed-overlay effect.

Chapter 9: Combining All Components in the App

The class assembled every component into the root App.js, wrapped in a top-level ScrollView so the full showcase is scrollable on any screen size.

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 available height even when content is shorter than the screen.

🧪 Try It Yourself

Task: Build a mini feedback form with a TextInput for a comment, a Button labelled "Submit", and a Modal that appears on submit displaying the comment back to the user.

Success criterion: Typing "Great app!" and pressing Submit opens a modal that reads "Your feedback: Great app!" with a "Close" button that dismisses it.

Starter snippet:

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

export default function FeedbackForm() {
  const [comment, setComment] = useState('');
  const [visible, setVisible] = useState(false);

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.input}
        placeholder="Leave a comment…"
        value={comment}
        onChangeText={setComment}
      />
      <Button title="Submit" onPress={() => setVisible(true)} />
      <Modal visible={visible} transparent animationType="fade">
        <View style={styles.overlay}>
          {/* TODO: show "Your feedback: {comment}" and a Close button */}
        </View>
      </Modal>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { padding: 20 },
  input: { height: 40, borderWidth: 1, borderColor: '#aaa', paddingHorizontal: 8, marginBottom: 12 },
  overlay: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: 'rgba(0,0,0,0.4)' },
});

🔍 Checkpoint Quiz

Q1. In React Native, why must you use StyleSheet.create() instead of plain JavaScript objects for styles?

A) It converts styles into real CSS
B) It validates style keys at startup and optimises the bridge transfer
C) It is required by the Metro bundler
D) It automatically adds vendor prefixes

Q2. Given this snippet, what does the user see after 3 seconds?

const [loading, setLoading] = useState(true);
useEffect(() => {
  setTimeout(() => setLoading(false), 3000);
}, []);
return loading ? <ActivityIndicator size="large" color="#0000ff" /> : <Text>Done!</Text>;

A) A blue spinner forever
B) The spinner disappears and "Done!" appears
C) The spinner disappears and nothing renders
D) A red spinner for 3 seconds

Q3. The following Image component renders a blank white box. What is the most likely cause?

<Image source={{ uri: 'https://example.com/photo.jpg' }} />

A) Remote URIs are not supported
B) No width and height were provided in the style
C) The source prop requires require() syntax
D) Image must be inside a ScrollView

Q4. How would you modify ModalComponent so that pressing the Android hardware back button closes the modal instead of doing nothing?

A) Add hardwareBackPress={true} to the Modal props
B) Implement onRequestClose to call setModalVisible(false)
C) Wrap the modal in a BackHandler component
D) Use animationType="none" to suppress the back gesture

A1. B — StyleSheet.create() validates property names at initialisation time and serialises the style object once, reducing repeated bridge overhead on every render.

A2. B — After 3000 ms, setLoading(false) triggers a re-render. The ternary now returns <Text>Done!</Text> instead of the spinner.

A3. B — React Native cannot infer remote image dimensions from the network at layout time. Without explicit width and height style values, the image collapses to 0×0 and is invisible.

A4. B — The onRequestClose prop is the designated handler for the Android back button (and iOS swipe-down on sheets). Setting it to () => setModalVisible(false) closes the modal correctly.

🪞 Recap

  • StyleSheet.create() is the idiomatic way to define styles in React Native — it uses JavaScript objects but validates and optimises them for the native bridge.
  • TextInput is a controlled component: store its value in useState and pass it back via the value prop.
  • ScrollView renders all children immediately and is ideal for short, mixed-content screens; use FlatList for long homogeneous lists.
  • ActivityIndicator displays a native platform spinner; pair it with a loading boolean in state to replace it with real content once data arrives.
  • Modal floats above the rest of the UI and requires onRequestClose to handle the Android back button correctly.

📚 Further Reading

Like this topic? It’s one of 56 in Full Stack Advanced.

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