1.2 Core Components
Learning Objectives
- Understand what React components are and how JSX works in React Native
- Build layouts using
Viewcontainers with flexbox properties - Render text content with the
Textcomponent and inline styles - Apply
SafeAreaViewto handle notches and status bars on mobile devices
What is a Component?
Section titled “What is a Component?”In React, a component is a JavaScript function that returns UI. This is the core idea behind React. Instead of manually manipulating the DOM or native views, you write functions that describe what the UI should look like.
JSX: JavaScript That Looks Like HTML
Section titled “JSX: JavaScript That Looks Like HTML”In React Native, you write UI using JSX, which looks like HTML but is actually JavaScript:
// This is a React componentexport default function App() { return ( <View> <Text>Hello, React Native!</Text> </View> );}When this code runs, React converts it into actual mobile UI elements. On iOS, <View> becomes a native UIView. On Android, it becomes a native android.view.ViewGroup. The JavaScript stays JavaScript, but the UI is 100% native.
Components Are Functions
Section titled “Components Are Functions”Every component is simply a function. React calls your function and uses the returned JSX to build the user interface:
// Step 1: Define a component functionfunction MyComponent() { return <Text>I'm a component!</Text>;}
// Step 2: Use it in JSXexport default function App() { return ( <View> <MyComponent /> {/* React calls MyComponent() here */} </View> );}React re-runs your component function whenever data changes, updating the UI automatically. This is the magic of React.
<View> 1 is the most fundamental container in React Native. It’s equivalent to <div> on the web, a box that holds other components and controls their layout using flexbox.
The style Prop
Section titled “The style Prop”In React Native, styles are JavaScript objects passed to the style prop. Unlike CSS files, you write styles inline using camelCase property names and numeric values (no units needed, all dimensions are in density-independent pixels):
<View style={{ flex: 1, backgroundColor: 'lightblue', padding: 20, borderRadius: 10, }}/>Key differences from web CSS:
| Web CSS | React Native | Notes |
|---|---|---|
background-color | backgroundColor | camelCase property names |
margin: 20px | margin: 20 | No units (all values in dp/pt) |
border-radius | borderRadius | Numbers automatically in points |
flex-direction | flexDirection | Same flexbox, but camelCase |
Flexbox Basics
Section titled “Flexbox Basics”React Native uses the same flexbox layout model as CSS web, but with a key difference: the default flexDirection is column (vertical stacking) instead of row (horizontal). Here are the most common properties:
| Property | Purpose | Example Values |
|---|---|---|
flex | Grows to fill available space | flex: 1 (fills all space) |
flexDirection | Arranges children vertically or horizontally | 'column' (default), 'row' |
justifyContent | Aligns children along main axis | 'center', 'space-between' |
alignItems | Aligns children perpendicular to main | 'center', 'flex-start' |
gap | Spacing between children | 10, 20 (in points) |
The flex: 1 Pattern
Section titled “The flex: 1 Pattern”A critical pattern in React Native is flex: 1 on a View. Without it, containers have zero height and collapse:
// ❌ This View has no height! Content is invisible.<View style={{ backgroundColor: 'blue' }}> <Text>I'm invisible!</Text></View>
// ✅ This View fills available space<View style={{ flex: 1, backgroundColor: 'blue' }}> <Text>Now I'm visible!</Text></View>Deeper Dive: Flexbox in React Native vs Web
React Native uses the same flexbox 2 layout model as CSS, but with a few key differences:
- Default direction:
flexDirection: 'column'(notrowlike web) so children stack vertically by default. - Size requirements: Must use
flex: 1on root View to fill the screen. Without it, the container has no height. - No wrapping: No automatic wrapping, so you must explicitly set
flexWrap: 'wrap'. - Everything else:
justifyContent,alignItems,gap,marginBottom, etc. work exactly like CSS flexbox.
Common Pattern
Section titled “Common Pattern”<View style={{ flex: 1 }}>{/* Content fills available space */}</View><Text> 3 is the only component in React Native for displaying text. Unlike web where you can put text directly in a <div>, mobile apps require explicit <Text> elements. This is because text needs special rendering optimization on mobile platforms.
Inline Styles on Text
Section titled “Inline Styles on Text”Text supports the same inline styling as View, plus text-specific properties:
<Text style={{ fontSize: 16, fontWeight: 'bold', color: '#333', marginBottom: 10, }}> Styled text</Text>Common text properties:
| Property | Purpose | Example Values |
|---|---|---|
fontSize | Text size in points | 12, 16, 20 |
fontWeight | Text boldness | 'normal', 'bold', '600' |
color | Text color | '#333', 'white', 'red' |
lineHeight | Space between lines | 20, 24 (in points) |
textAlign | Horizontal alignment | 'left', 'center', 'right' |
Nesting Text for Formatting
Section titled “Nesting Text for Formatting”You can nest <Text> components to create formatted text, similar to <span> tags on the web. Each nested <Text> can have its own styles:
<Text> I am normal <Text style={{ fontWeight: 'bold' }}>and I am bold</Text> <Text style={{ color: 'red', fontWeight: 'bold' }}> {' '} and I am bold and red </Text></Text>Behind the scenes, React Native converts nested text into native attributed strings (NSAttributedString on iOS, SpannableString on Android), so you get platform-native text rendering with inline formatting.
Deeper Dive: Text Inheritance and Custom Text Components
Style Inheritance (Limited to Text Subtrees)
Section titled “Style Inheritance (Limited to Text Subtrees)”Unlike the web where CSS properties cascade through the entire DOM, React Native only allows style inheritance within <Text> component subtrees:
// Parent styles are inherited by nested Text<Text style={{ fontWeight: 'bold', fontSize: 16 }}> I am bold and 16pt <Text style={{ color: 'red' }}> {' '} and I inherit bold and fontSize, plus I'm red </Text></Text>This inheritance only works for <Text> components. You cannot set a font on a <View> and expect child <Text> components to inherit it:
// ❌ This does NOT work - fontFamily on View is ignored<View style={{ fontFamily: 'Helvetica' }}> <Text>This won't be Helvetica</Text></View>
// ✅ Instead, set styles directly on Text or create a wrapper component<MyAppText>This will have consistent styling</MyAppText>Creating Custom Text Components
Section titled “Creating Custom Text Components”Since there’s no global font inheritance, create wrapper components for consistent typography:
import { Text } from 'react-native';
export function MyAppText({ children, style }: any) { return ( <Text style={[{ fontFamily: 'Roboto', fontSize: 14 }, style]}> {children} </Text> );}
// MyAppHeaderText.tsx - Reuse for specific purposesexport function MyAppHeaderText({ children }: any) { return ( <MyAppText style={{ fontSize: 20, fontWeight: 'bold' }}> {children} </MyAppText> );}This gives you consistent typography while still allowing style overrides at the component level.
SafeAreaView
Section titled “SafeAreaView”Modern mobile devices have hardware elements (notches, rounded corners, status bars, clocks) that can obscure your content if you’re not careful. Safe areas define the portions of the screen where your content is guaranteed to be visible and not covered by these interface elements.
<SafeAreaView> from the react-native-safe-area-context library is a <View> component that automatically applies padding to keep your content within safe boundaries. Instead of manually calculating insets for each device, you simply wrap your screen content in SafeAreaView.
import { SafeAreaView } from 'react-native-safe-area-context';
export default function HomeScreen() { return ( <SafeAreaView style={{ flex: 1 }}> <Text>Content is in safe area.</Text> </SafeAreaView> );}In Expo, react-native-safe-area-context comes pre-installed. If needed, install it with:
bunx expo install react-native-safe-area-contextDeeper Dive: How SafeAreaProvider Works
The react-native-safe-area-context library has two parts:
SafeAreaProvider
Section titled “SafeAreaProvider”Wraps your entire app at the root level (usually in App.tsx or app/_layout.tsx). It measures the device’s safe area insets once and provides them to all child components:
// App.tsx or app/_layout.tsximport { SafeAreaProvider } from 'react-native-safe-area-context';
export default function App() { return <SafeAreaProvider>{/* Your app content */}</SafeAreaProvider>;}SafeAreaView
Section titled “SafeAreaView”Used in individual screens to apply the safe area padding. It’s a regular <View> that automatically adds padding based on the insets provided by SafeAreaProvider.
// A screen componentimport { SafeAreaView } from 'react-native-safe-area-context';
export default function HomeScreen() { return ( <SafeAreaView style={{ flex: 1 }}> <Text>Content is safe.</Text> </SafeAreaView> );}React Context
Section titled “React Context”SafeAreaProvider uses React Context to share safe area measurements with all child components. You’ll learn Context in detail later in the course. For now, just know that it allows data to be “teleported” from a parent provider to any descendant component without passing props through every layer.
This is why we need SafeAreaProvider at the app root and can use SafeAreaView anywhere below it!
Exercises
Section titled “Exercises”To complete these exercises, follow these steps:
- Open the Snack by clicking the icon in the toolbar.
- If not already logged in, create a free Expo account and sign in.
- Click the blue Save button to fork the Snack to your account.
- Make changes to the code and save your work.
- Submit on Moodle by copy-pasting the Snack URL into the submission box.
Build a 3×3 grid of colored boxes using View and flexbox.
- Use
flex: 1on each box to make them fill their container equally - Use
flexDirection: 'row'to arrange boxes horizontally in each row - Use
gap: 10to add spacing between boxes - Choose your own colors using hex codes like
#FF6B6B - Optional: Add
borderRadiusto round the corners
Create a welcome card layout with:
- A header section with a background color
- A title (
Textin large, bold font) - A subtitle (
Textin smaller, gray font) - A body paragraph
- Use
flexDirectionto arrange sections vertically - Use inline styles for colors and spacing
Hint: Start with a root View with flex: 1, then nest Views inside for each section.
Hint
For the grid layout, think about how to organize the rows. Each row should be a View with flex: 1 and flexDirection: 'row'. Inside each row, place three boxes, each with flex: 1 and a different color.
Key pattern:
<View style={{ flex: 1, flexDirection: 'row', gap: 10 }}> <View style={{ flex: 1, backgroundColor: '#FF6B6B' }} /> <View style={{ flex: 1, backgroundColor: '#4ECDC4' }} /> <View style={{ flex: 1, backgroundColor: '#FFD93D' }} /></View>Structure your card like this:
<SafeAreaView style={{ flex: 1, backgroundColor: '#f5f5f5' }}> {/* Header */} <View style={{ backgroundColor: '#536DFE', padding: 20 }} />
{/* Body - scrollable content */} <View style={{ flex: 1, padding: 20 }}> <Text style={{ fontSize: 24, fontWeight: 'bold' }}>Title</Text> <Text style={{ fontSize: 14, color: '#999', marginTop: 10 }}>Subtitle</Text> <Text style={{ marginTop: 20, lineHeight: 22 }}>Body text here...</Text> </View></SafeAreaView>Summary
Section titled “Summary”- Components are functions that return JSX describing the UI.
- View is the container; use
flex: 1to fill space and flexbox for layout. - Text is the only way to display text; wrap all text in
<Text>components. - SafeAreaView automatically handles notches and status bars on mobile devices.
- Inline styles are JavaScript objects with camelCase properties.
- Flexbox in React Native works like web flexbox, but defaults to vertical (
column) layout.