📦 View: The Universal Container
The fundamental building block of every React Native layout
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Understand what View is and why it's the foundation of React Native layouts
- Explain the key differences between View and HTML's div
- Use common View props for styling, accessibility, and interaction
- Apply Flexbox basics to arrange child components
- Nest Views to create complex layout structures
⏱️ Estimated Time: 30-40 minutes
📑 In This Lesson
What is View?
In React Native, View is the most fundamental component. It's a container that supports layout with Flexbox, styling, touch handling, and accessibility controls. Every screen you build will be composed primarily of Views.
📖 Definition
View is a container component that maps directly to the native view equivalent on each platform — UIView on iOS, android.view.View on Android, and <div> on web.
Think of View as the LEGO brick of React Native. Just as you can build anything from LEGO by combining simple bricks, you build any interface in React Native by combining and nesting Views.
Your First View
Here's the simplest possible View:
import { View } from 'react-native';
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: '#f0f0f0' }}>
{/* Your content goes here */}
</View>
);
}
This creates a full-screen container with a light gray background. The flex: 1 tells the View to expand and fill all available space — we'll explore why this works in the Flexbox section.
Every screen is built by composing Views — from simple layouts to complex UIs
What View Does
View handles several responsibilities that you might associate with different HTML elements:
📐 Layout Container
Arranges children using Flexbox. Every View is a flex container by default.
🎨 Styling Target
Accepts styles for background, borders, shadows, spacing, and more.
👆 Touch Responder
Can respond to touch events and gestures when needed.
♿ Accessibility Container
Groups content for screen readers and accessibility services.
View vs HTML div
If you're coming from web development, you might think of View as React Native's version of <div>. That's a reasonable starting point, but there are important differences.
Key Differences
| Aspect | HTML div | React Native View |
|---|---|---|
| Default display | display: block |
display: flex with flexDirection: 'column' |
| Text content | Can contain raw text directly | ❌ Cannot contain raw text — must use Text component |
| Styling | CSS classes, inline styles, CSS-in-JS | StyleSheet objects or inline style objects |
| Units | px, em, rem, %, vh, vw, etc. | Unitless numbers (density-independent pixels) or % |
| Scrolling | overflow: scroll |
❌ View doesn't scroll — use ScrollView |
| Click handling | onClick |
Use Pressable or TouchableOpacity instead |
⚠️ The #1 Gotcha: No Raw Text!
This will crash your app:
// ❌ WRONG - This will crash!
<View>
Hello World
</View>
You must always wrap text in a Text component:
// ✅ CORRECT
<View>
<Text>Hello World</Text>
</View>
Mental Model Shift
On the web, you might write:
<div class="card">
<h2>Title</h2>
<p>Some content here</p>
<button onclick="handleClick()">Click me</button>
</div>
In React Native, the equivalent is:
<View style={styles.card}>
<Text style={styles.title}>Title</Text>
<Text style={styles.content}>Some content here</Text>
<Pressable onPress={handlePress}>
<Text>Click me</Text>
</Pressable>
</View>
Notice how every piece of text needs a Text component, and the button becomes a Pressable wrapping a Text. This explicitness might feel verbose at first, but it gives you precise control over every element.
flowchart LR
subgraph Web["Web (HTML/CSS)"]
A["<div>"] --> B["Block layout default"]
A --> C["Can contain text"]
A --> D["overflow: scroll"]
A --> E["onClick"]
end
subgraph Native["React Native"]
F["<View>"] --> G["Flex layout default"]
F --> H["Needs <Text>"]
F --> I["Use ScrollView"]
F --> J["Use Pressable"]
end
style Web fill:#fff3e0,stroke:#ff9800
style Native fill:#e3f2fd,stroke:#1976d2
View is similar to div but with key behavioral differences
Flexbox by Default
One of the most important things to understand about View is that it's a Flexbox container by default. Unlike web CSS where you need to explicitly set display: flex, every View automatically uses Flexbox for layout.
✅ Key Insight
In React Native, Flexbox isn't optional — it's the only layout system. There's no display: block, no float, no position: absolute as your primary layout tool. Flexbox handles everything.
The Default Flex Direction
Here's a crucial difference from web Flexbox:
| Property | Web Default | React Native Default |
|---|---|---|
flexDirection |
row (horizontal) |
column (vertical) |
This makes sense for mobile — most mobile layouts flow vertically (top to bottom), so column is a sensible default.
React Native's column default makes sense for mobile's vertical scroll pattern
Basic Flexbox Example
import { View, Text, StyleSheet } from 'react-native';
export default function FlexExample() {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text>Box 1</Text>
</View>
<View style={styles.box}>
<Text>Box 2</Text>
</View>
<View style={styles.box}>
<Text>Box 3</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
// flexDirection: 'column' is the default
padding: 20,
backgroundColor: '#f5f5f5',
},
box: {
backgroundColor: '#2196F3',
padding: 20,
marginBottom: 10,
borderRadius: 8,
},
});
This creates three blue boxes stacked vertically — no need to set flexDirection because column is the default.
The flex Property
You'll see flex: 1 everywhere in React Native. Here's what it means:
flex: 1— Take up all available space in the parent's main axisflex: 2— Take up twice as much space asflex: 1siblingsflex: 0— Only take up the space needed for content (this is the default)
<View style={{ flex: 1 }}>
<View style={{ flex: 1, backgroundColor: 'red' }} />
<View style={{ flex: 2, backgroundColor: 'green' }} />
<View style={{ flex: 1, backgroundColor: 'blue' }} />
</View>
This creates a screen divided into 4 equal parts: red gets 1 part (25%), green gets 2 parts (50%), and blue gets 1 part (25%).
flex values determine how siblings share available space
Common View Props
View accepts a rich set of props that control its appearance, behavior, and accessibility. Let's explore the most important ones.
Style Props
The style prop accepts either an object or an array of objects:
// Single style object
<View style={{ padding: 20, backgroundColor: 'white' }}>
// Using StyleSheet (recommended)
<View style={styles.container}>
// Combining styles (array)
<View style={[styles.container, styles.shadow, { marginTop: 10 }]}>
// Conditional styles
<View style={[styles.box, isActive && styles.activeBox]}>
✅ Pro Tip: Style Arrays
When you pass an array of styles, later styles override earlier ones. This is powerful for creating base styles with conditional modifications:
style={[styles.base, isError && styles.error]}
If isError is false, only styles.base applies. If true, styles.error overrides any conflicting properties.
Common Style Properties
Here are the style properties you'll use most often with View:
const styles = StyleSheet.create({
exampleView: {
// Layout
flex: 1,
flexDirection: 'row', // 'column' | 'row'
justifyContent: 'center', // main axis alignment
alignItems: 'center', // cross axis alignment
// Spacing
padding: 20, // or paddingHorizontal, paddingVertical
margin: 10, // or marginHorizontal, marginVertical
gap: 10, // space between children
// Sizing
width: 200, // fixed width
height: 100, // fixed height
minWidth: 100, // minimum width
maxHeight: 500, // maximum height
// Background
backgroundColor: '#ffffff',
opacity: 0.9,
// Borders
borderWidth: 1,
borderColor: '#cccccc',
borderRadius: 8, // rounded corners
borderTopLeftRadius: 12, // individual corners
// Shadows (iOS)
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 3.84,
// Shadows (Android)
elevation: 5,
// Positioning (when needed)
position: 'absolute', // 'relative' is default
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: 10,
},
});
Layout Props Reference
| Property | Values | Description |
|---|---|---|
flexDirection |
'column', 'row', 'column-reverse', 'row-reverse' |
Direction children are laid out |
justifyContent |
'flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'space-evenly' |
Alignment along main axis |
alignItems |
'flex-start', 'flex-end', 'center', 'stretch', 'baseline' |
Alignment along cross axis |
flexWrap |
'nowrap', 'wrap', 'wrap-reverse' |
Whether children wrap to next line |
alignSelf |
Same as alignItems | Override parent's alignItems for this child |
gap |
number | Space between children (newer RN versions) |
Non-Style Props
View also accepts props that aren't about styling:
<View
// Identification
testID="main-container" // For testing frameworks
nativeID="native-main" // Native view identifier
// Touch handling (basic)
pointerEvents="auto" // 'auto' | 'none' | 'box-none' | 'box-only'
// Rendering
collapsable={false} // Prevent view from being removed in optimization
needsOffscreenAlphaCompositing // For complex opacity/transform animations
// Layout callbacks
onLayout={(event) => {
const { x, y, width, height } = event.nativeEvent.layout;
console.log('View dimensions:', width, height);
}}
>
The pointerEvents Prop
This prop controls how the View handles touch events:
| Value | Behavior |
|---|---|
'auto' |
Default — View can receive touches |
'none' |
View and children ignore touches (pass through) |
'box-none' |
View ignores touches, but children can receive them |
'box-only' |
View receives touches, but children don't |
This is useful for overlay UIs where you want touches to pass through to content below:
// Overlay that doesn't block touches on the content below
<View style={styles.overlay} pointerEvents="box-none">
<View style={styles.floatingButton}>
{/* This button CAN receive touches */}
</View>
</View>
Nesting Views
Complex layouts are built by nesting Views inside each other. Each View creates its own Flexbox context, so children arrange themselves relative to their immediate parent.
Building a Card Component
Let's build a typical card layout step by step:
import { View, Text, Image, StyleSheet } from 'react-native';
function ProductCard({ product }) {
return (
<View style={styles.card}>
{/* Image section */}
<View style={styles.imageContainer}>
<Image source={{ uri: product.image }} style={styles.image} />
{product.onSale && (
<View style={styles.saleBadge}>
<Text style={styles.saleText}>SALE</Text>
</View>
)}
</View>
{/* Content section */}
<View style={styles.content}>
<Text style={styles.title}>{product.name}</Text>
<Text style={styles.description}>{product.description}</Text>
{/* Price row */}
<View style={styles.priceRow}>
<Text style={styles.price}>${product.price}</Text>
<View style={styles.rating}>
<Text>⭐ {product.rating}</Text>
</View>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 3,
margin: 10,
overflow: 'hidden', // Clips image to border radius
},
imageContainer: {
position: 'relative', // For positioning the sale badge
},
image: {
width: '100%',
height: 200,
},
saleBadge: {
position: 'absolute',
top: 10,
right: 10,
backgroundColor: '#ff4444',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 4,
},
saleText: {
color: 'white',
fontWeight: 'bold',
fontSize: 12,
},
content: {
padding: 16,
},
title: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 4,
},
description: {
color: '#666',
marginBottom: 12,
},
priceRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
price: {
fontSize: 20,
fontWeight: 'bold',
color: '#2196F3',
},
rating: {
flexDirection: 'row',
alignItems: 'center',
},
});
Each View creates its own layout context for positioning children
Common Nesting Patterns
🔲 Container → Sections
<View style={styles.screen}>
<View style={styles.header}>...</View>
<View style={styles.content}>...</View>
<View style={styles.footer}>...</View>
</View>
↔️ Horizontal Row
<View style={{ flexDirection: 'row' }}>
<View style={{ flex: 1 }}>...</View>
<View style={{ flex: 1 }}>...</View>
</View>
📍 Absolute Overlay
<View style={{ position: 'relative' }}>
<Image ... />
<View style={{
position: 'absolute',
bottom: 10, left: 10
}}>...</View>
</View>
🎯 Centered Content
<View style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}}>
<Text>Centered!</Text>
</View>
Accessibility with View
View provides powerful accessibility props that help screen readers understand your UI. Building accessible apps isn't optional — it's essential for reaching all users.
Key Accessibility Props
<View
// Make the view accessible as a single unit
accessible={true}
// Label read by screen readers
accessibilityLabel="Product card for Blue Widget"
// Additional context after the label
accessibilityHint="Double tap to view product details"
// Role of this element (helps screen readers)
accessibilityRole="button" // or 'header', 'image', 'link', etc.
// Current state
accessibilityState={{
disabled: false,
selected: true,
checked: false, // for checkboxes
expanded: true, // for accordions
}}
// Value for sliders, progress bars, etc.
accessibilityValue={{
min: 0,
max: 100,
now: 75,
text: '75%',
}}
>
Grouping Content
When accessible={true}, the View and all its children are treated as a single accessible element. This is useful for grouping related content:
// Without accessible={true}, screen reader announces each element separately:
// "John Smith" ... "Software Engineer" ... "San Francisco"
// With accessible={true}, announces as one unit:
// "John Smith, Software Engineer, San Francisco"
<View
accessible={true}
accessibilityLabel="John Smith, Software Engineer, San Francisco"
>
<Text>John Smith</Text>
<Text>Software Engineer</Text>
<Text>San Francisco</Text>
</View>
Accessibility Roles
| Role | Use For |
|---|---|
'button' |
Tappable elements that trigger actions |
'link' |
Navigation to another screen or URL |
'header' |
Section headings |
'image' |
Images (combine with accessibilityLabel) |
'search' |
Search input fields |
'alert' |
Important messages that need attention |
'checkbox' |
Toggleable selection items |
'tab' |
Tab navigation items |
'tablist' |
Container for tabs |
'none' |
Decorative elements to skip |
💡 Accessibility Best Practices
- Always test with VoiceOver (iOS) or TalkBack (Android)
- Provide meaningful labels — "Submit form" not "Button 1"
- Use hints to explain non-obvious interactions
- Group related content with
accessible={true} - Hide decorative elements with
accessibilityRole="none" - Ensure touch targets are at least 44x44 points
Example: Accessible Card
function AccessibleProductCard({ product, onPress }) {
return (
<Pressable
onPress={onPress}
accessible={true}
accessibilityRole="button"
accessibilityLabel={`${product.name}, ${product.price} dollars, rated ${product.rating} stars`}
accessibilityHint="Double tap to view product details"
>
<View style={styles.card}>
<Image
source={{ uri: product.image }}
style={styles.image}
accessibilityRole="none" // Decorative, label covers it
/>
<View style={styles.content}>
<Text style={styles.title}>{product.name}</Text>
<Text style={styles.price}>${product.price}</Text>
<Text>⭐ {product.rating}</Text>
</View>
</View>
</Pressable>
);
}
Hands-On Exercises
Time to practice! These exercises will help cement your understanding of View.
Exercise 1: Basic Layout
Goal: Create a screen with a header, main content area, and footer using Views.
Requirements:
- Header takes up 10% of screen height with a blue background
- Content area takes up 80% with a white background
- Footer takes up 10% with a gray background
- Each section should have centered text indicating its name
💡 Hint
Use flex values of 1, 8, and 1 to achieve the 10/80/10 ratio. Remember to wrap text in Text components!
✅ Solution
import { View, Text, StyleSheet } from 'react-native';
export default function BasicLayout() {
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerText}>Header</Text>
</View>
<View style={styles.content}>
<Text style={styles.contentText}>Main Content</Text>
</View>
<View style={styles.footer}>
<Text style={styles.footerText}>Footer</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
header: {
flex: 1,
backgroundColor: '#2196F3',
justifyContent: 'center',
alignItems: 'center',
},
headerText: {
color: 'white',
fontSize: 18,
fontWeight: 'bold',
},
content: {
flex: 8,
backgroundColor: 'white',
justifyContent: 'center',
alignItems: 'center',
},
contentText: {
fontSize: 24,
color: '#333',
},
footer: {
flex: 1,
backgroundColor: '#666',
justifyContent: 'center',
alignItems: 'center',
},
footerText: {
color: 'white',
fontSize: 14,
},
});
Exercise 2: Two-Column Layout
Goal: Create a two-column layout where the left column is 1/3 width and right column is 2/3 width.
Requirements:
- Container should fill the screen
- Left sidebar: coral background, flex: 1
- Right main area: light gray background, flex: 2
- Each column should display its name centered
💡 Hint
You need to change flexDirection to 'row' on the container to make children arrange horizontally.
✅ Solution
import { View, Text, StyleSheet } from 'react-native';
export default function TwoColumnLayout() {
return (
<View style={styles.container}>
<View style={styles.sidebar}>
<Text style={styles.text}>Sidebar</Text>
</View>
<View style={styles.main}>
<Text style={styles.text}>Main Content</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row', // Key: horizontal layout
},
sidebar: {
flex: 1,
backgroundColor: 'coral',
justifyContent: 'center',
alignItems: 'center',
},
main: {
flex: 2,
backgroundColor: '#f0f0f0',
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: 18,
fontWeight: 'bold',
},
});
Exercise 3: Profile Card
Goal: Build a profile card with an avatar placeholder, name, title, and a row of stat boxes.
Requirements:
- Card with white background, rounded corners, and shadow
- Circular avatar placeholder (80x80, centered)
- Name (bold, larger text) and title (gray, smaller) below avatar
- Three stat boxes in a row: "Posts", "Followers", "Following"
- Each stat box shows a number above and label below
💡 Hint
Use borderRadius: 40 on a 80x80 View to create a circle. For the stats row, use flexDirection: 'row' with justifyContent: 'space-around'.
✅ Solution
import { View, Text, StyleSheet } from 'react-native';
export default function ProfileCard() {
return (
<View style={styles.container}>
<View style={styles.card}>
{/* Avatar */}
<View style={styles.avatar}>
<Text style={styles.avatarText}>👤</Text>
</View>
{/* Name and Title */}
<Text style={styles.name}>Jane Developer</Text>
<Text style={styles.title}>Senior React Native Engineer</Text>
{/* Stats Row */}
<View style={styles.statsRow}>
<View style={styles.statBox}>
<Text style={styles.statNumber}>142</Text>
<Text style={styles.statLabel}>Posts</Text>
</View>
<View style={styles.statBox}>
<Text style={styles.statNumber}>10.5K</Text>
<Text style={styles.statLabel}>Followers</Text>
</View>
<View style={styles.statBox}>
<Text style={styles.statNumber}>284</Text>
<Text style={styles.statLabel}>Following</Text>
</View>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5f5f5',
padding: 20,
},
card: {
backgroundColor: 'white',
borderRadius: 16,
padding: 24,
alignItems: 'center',
width: '100%',
maxWidth: 350,
// Shadow for iOS
shadowColor: '#000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.1,
shadowRadius: 12,
// Shadow for Android
elevation: 5,
},
avatar: {
width: 80,
height: 80,
borderRadius: 40,
backgroundColor: '#e0e0e0',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 16,
},
avatarText: {
fontSize: 36,
},
name: {
fontSize: 22,
fontWeight: 'bold',
color: '#333',
marginBottom: 4,
},
title: {
fontSize: 14,
color: '#888',
marginBottom: 20,
},
statsRow: {
flexDirection: 'row',
justifyContent: 'space-around',
width: '100%',
borderTopWidth: 1,
borderTopColor: '#eee',
paddingTop: 20,
},
statBox: {
alignItems: 'center',
},
statNumber: {
fontSize: 20,
fontWeight: 'bold',
color: '#2196F3',
},
statLabel: {
fontSize: 12,
color: '#888',
marginTop: 4,
},
});
Challenge: Nested Grid
🏆 Bonus Challenge
Goal: Create a 2x2 grid of colored boxes that fills the screen, with 10px gaps between them.
Hint: You'll need to nest Views — one outer container with flexDirection: 'column', containing two row Views, each with two box Views.
✅ Solution
import { View, StyleSheet } from 'react-native';
export default function Grid() {
return (
<View style={styles.container}>
<View style={styles.row}>
<View style={[styles.box, { backgroundColor: '#f44336' }]} />
<View style={[styles.box, { backgroundColor: '#4caf50' }]} />
</View>
<View style={styles.row}>
<View style={[styles.box, { backgroundColor: '#2196f3' }]} />
<View style={[styles.box, { backgroundColor: '#ff9800' }]} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 10,
gap: 10, // or use margin on children
},
row: {
flex: 1,
flexDirection: 'row',
gap: 10,
},
box: {
flex: 1,
borderRadius: 8,
},
});
Summary
🎉 Key Takeaways
- View is the universal container — it's the building block for all layouts in React Native
- View ≠ div — Views are Flexbox containers by default, can't contain raw text, and don't scroll
- flexDirection defaults to 'column' — this differs from web CSS where it defaults to 'row'
- Use flex for proportional sizing —
flex: 1fills available space,flex: 2takes twice as much asflex: 1 - Nest Views for complex layouts — each View creates its own Flexbox context
- Accessibility matters — use
accessible,accessibilityLabel, andaccessibilityRoleprops
Quick Reference
import { View, Text, StyleSheet } from 'react-native';
// Basic View with common props
<View
style={styles.container}
accessible={true}
accessibilityLabel="Main content area"
accessibilityRole="main"
onLayout={(e) => console.log(e.nativeEvent.layout)}
testID="main-view"
>
<Text>Content must be in Text components</Text>
</View>
// Essential styles
const styles = StyleSheet.create({
container: {
flex: 1, // Fill available space
flexDirection: 'column', // Vertical layout (default)
justifyContent: 'center', // Center on main axis
alignItems: 'center', // Center on cross axis
padding: 20,
backgroundColor: '#fff',
borderRadius: 8,
},
});
🚀 What's Next?
Now that you understand View, it's time to learn about the Text component — how to display and style text in React Native, which works quite differently from web typography.
📦 Foundation Laid!
You now understand the most fundamental component in React Native. Everything you build will use Views as containers — this knowledge will serve you in every screen you create!