🧠 Web vs Native Mental Models
The conceptual shifts that will make everything else click
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Explain why there's no DOM, browser, or traditional CSS in React Native
- Map HTML elements to their React Native component equivalents
- Describe how the bridge architecture connects JavaScript to native code
- Recognize key differences between iOS and Android platforms
- Set realistic performance expectations for React Native apps
⏱️ Estimated Time: 35-45 minutes
📑 In This Lesson
No DOM, No Browser, No CSS
Here's the biggest mental shift you need to make: React Native doesn't run in a browser. There's no DOM. There's no window object. There's no document. The entire web platform you've been building on? It doesn't exist here.
Think of it like this: as a web developer, you've been building houses using a specific construction method — wood framing, drywall, the works. React Native is asking you to build houses using a completely different method — maybe steel and concrete. The end result is still a house, but the materials and techniques are different.
🌐 What's Missing from the Web Platform
document— No document object, no querySelector, no getElementByIdwindow— No window object, no localStorage (use AsyncStorage instead)CSS files— No importing .css files, no CSS classesHTML tags— No div, span, p, img, input, etc.CSS cascade— Styles don't inherit the way they do in CSSBrowser DevTools— No Elements panel, no CSS inspector (different tools)
Wait, Then How Do We Style Things?
Instead of CSS, React Native uses JavaScript objects that look CSS-ish but follow different rules. Here's a taste:
// ❌ This is NOT how React Native works
import './styles.css';
function Card() {
return <div className="card">Hello</div>;
}
// ✅ This IS how React Native works
import { View, Text, StyleSheet } from 'react-native';
function Card() {
return (
<View style={styles.card}>
<Text>Hello</Text>
</View>
);
}
const styles = StyleSheet.create({
card: {
backgroundColor: '#fff', // camelCase, not kebab-case
padding: 16, // numbers, not '16px'
borderRadius: 8,
shadowColor: '#000', // shadows work differently
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3, // Android-specific shadow
},
});
Notice the differences? Properties are camelCase (backgroundColor not background-color). Values are numbers, not strings with units. And there's no CSS cascade — styles don't flow down to children the way they do on the web.
The Architecture Difference
Web React runs in a browser with DOM; React Native runs directly on the device with native UI
💡 The Good News
While the platform is different, React itself works exactly the same. Components, props, state, hooks, context — all of it transfers. You're not learning a new paradigm; you're learning a new rendering target.
Native Components vs HTML Elements
In React for web, you use HTML elements: <div>, <span>, <p>, <img>, <input>. In React Native, you use native components that map to real platform widgets.
Think of it like language translation. "Hello" in English becomes "Hola" in Spanish — same meaning, different expression. <div> in web becomes <View> in React Native — same concept (a container), different implementation.
The Translation Table
| Web (HTML) | React Native | Notes |
|---|---|---|
<div> |
<View> |
Generic container. Use for layout. |
<span>, <p>, <h1> |
<Text> |
ALL text must be in Text. No exceptions! |
<img> |
<Image> |
Uses source prop, not src |
<input> |
<TextInput> |
No type attribute; different props |
<button> |
<Pressable> or <Button> |
Pressable is more flexible |
<a> |
<Link> (Expo Router) or <Pressable> |
Navigation works differently |
<ul>, <li> |
<FlatList> or <ScrollView> |
Lists are virtualized for performance |
<form> |
<View> |
No form element; handle submission manually |
<label> |
<Text> |
Use accessibility props for association |
| Scroll container | <ScrollView> |
Content doesn't scroll by default! |
The Cardinal Rule: Text Must Be in Text
This trips up every web developer at first. In HTML, you can put text anywhere:
<!-- ✅ Web: This is fine -->
<div>Hello World</div>
<span>Some text</span>
In React Native, all text must be wrapped in a Text component:
// ❌ React Native: This will CRASH
<View>Hello World</View>
// ✅ React Native: This is correct
<View>
<Text>Hello World</Text>
</View>
⚠️ Common Early Error
If you see an error like "Text strings must be rendered within a <Text> component", you've got naked text somewhere. Check for text directly inside View, Pressable, or other non-Text components.
A Side-by-Side Example
Let's build a simple card component in both React (web) and React Native:
// 🌐 React for Web
function ProfileCard({ name, role, avatar }) {
return (
<div className="card">
<img src={avatar} alt={name} className="avatar" />
<div className="info">
<h2>{name}</h2>
<p>{role}</p>
</div>
<button onClick={() => alert('Followed!')}>
Follow
</button>
</div>
);
}
// 📱 React Native
import { View, Text, Image, Pressable, StyleSheet } from 'react-native';
function ProfileCard({ name, role, avatar }) {
return (
<View style={styles.card}>
<Image source={{ uri: avatar }} style={styles.avatar} />
<View style={styles.info}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.role}>{role}</Text>
</View>
<Pressable
style={styles.button}
onPress={() => Alert.alert('Followed!')}
>
<Text style={styles.buttonText}>Follow</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
card: { flexDirection: 'row', padding: 16, backgroundColor: '#fff' },
avatar: { width: 60, height: 60, borderRadius: 30 },
info: { flex: 1, marginLeft: 12 },
name: { fontSize: 18, fontWeight: 'bold' },
role: { fontSize: 14, color: '#666' },
button: { backgroundColor: '#3b82f6', padding: 8, borderRadius: 4 },
buttonText: { color: '#fff', fontWeight: '600' },
});
See the pattern? The structure is similar, but the building blocks are different. And notice how every piece of text is wrapped in <Text>, including the button label.
The Bridge Architecture
Here's something important to understand: your JavaScript code and the native UI run in separate worlds. They communicate through what's called the "bridge."
Imagine you're in a restaurant kitchen (JavaScript) and need to serve food to customers in the dining room (native UI). You can't walk out there yourself — you have to put plates on a conveyor belt (the bridge) that carries them to the other side. The waiter (native side) picks them up and serves them.
flowchart LR
subgraph JS["JavaScript Thread"]
A[Your React Code] --> B[React Native Runtime]
end
subgraph Bridge["Bridge"]
C[JSON Messages]
end
subgraph Native["Native Thread"]
D[Native Modules]
E[UI Manager]
end
B <--> C
C <--> D
C <--> E
E --> F[("📱 Screen")]
style JS fill:#fff3e0,stroke:#ff9800
style Bridge fill:#e3f2fd,stroke:#1976d2
style Native fill:#e8f5e9,stroke:#388e3c
The classic bridge architecture (being replaced by the New Architecture)
Why This Matters to You
In practice, you rarely think about the bridge directly. But understanding it explains a few things:
- Animations can feel janky if they go through the bridge on every frame (60 times per second!). That's why React Native has special animation APIs that run on the native side.
- Gestures need special handling — you can't just use mouse events. Touch gestures are native, so there are dedicated gesture libraries.
- Some operations are async when you'd expect them to be sync. Reading dimensions, for example, requires a round-trip through the bridge.
✅ The New Architecture Changes This
The "New Architecture" (which we'll cover more later) replaces the async JSON bridge with JSI (JavaScript Interface) — a way for JavaScript to directly call native code synchronously. This eliminates most bridge-related performance concerns. If you're starting fresh in 2024, you'll mostly benefit from this automatically.
What Runs Where?
flowchart TB
subgraph JSThread["JavaScript Thread"]
A["Your Components"]
B["Business Logic"]
C["State Management"]
D["API Calls"]
end
subgraph UIThread["UI/Main Thread"]
E["Layout Calculation"]
F["Rendering"]
G["Touch Handling"]
H["Animations*"]
end
subgraph BGThreads["Background Threads"]
I["Image Decoding"]
J["Network Requests"]
K["Native Modules"]
end
JSThread <--> UIThread
UIThread <--> BGThreads
style JSThread fill:#fff3e0,stroke:#ff9800
style UIThread fill:#e8f5e9,stroke:#388e3c
style BGThreads fill:#e3f2fd,stroke:#1976d2
*With Reanimated, animations run entirely on the UI thread for smooth 60fps
Platform Differences: iOS vs Android
React Native gives you one codebase, but it doesn't give you one platform. iOS and Android are different operating systems with different design languages, different capabilities, and different user expectations.
Think of it like writing a book that will be translated into French and Japanese. The story is the same, but some phrases need to be adapted for cultural context. Similarly, your app logic stays the same, but some UI details need platform-specific treatment.
iOS and Android have distinct design languages and user expectations
How React Native Handles Platform Differences
React Native gives you several tools for platform-specific code:
import { Platform, StyleSheet } from 'react-native';
// 1. Platform.OS - simple checks
if (Platform.OS === 'ios') {
// iOS-specific code
}
// 2. Platform.select - inline conditional
const styles = StyleSheet.create({
container: {
...Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
android: {
elevation: 4,
},
}),
},
});
// 3. Platform-specific files
// MyComponent.ios.js ← Used on iOS
// MyComponent.android.js ← Used on Android
// Import just says: import MyComponent from './MyComponent';
💡 The 80/20 Rule
In practice, about 80-90% of your code will be shared between platforms. The platform-specific bits are usually shadows/elevation, navigation patterns, and a few edge cases. Don't overthink it early on — you'll develop intuition for what needs to be different.
Key Differences to Know
| Aspect | iOS | Android |
|---|---|---|
| Shadows | shadowColor, shadowOffset, etc. |
elevation |
| Fonts | SF Pro (system font) | Roboto (system font) |
| Back Navigation | Swipe from left edge | Hardware/gesture back |
| Status Bar | Light/dark content | Light/dark + translucent options |
| Text Input | Keyboard avoidance automatic | May need KeyboardAvoidingView |
| Ripple Effect | Not native | android_ripple prop |
Performance Expectations and Realities
Let's address the elephant in the room: Is React Native fast enough?
The short answer: Yes, for the vast majority of apps.
The longer answer: React Native performance has improved dramatically, especially with the New Architecture. For standard app features — lists, forms, navigation, API calls, images — React Native is indistinguishable from native. Users cannot tell the difference.
Where Performance Matters
flowchart LR
subgraph Excellent["Excellent Performance"]
A["Forms & Inputs"]
B["Navigation"]
C["API Integration"]
D["Standard Lists"]
end
subgraph Good["Good Performance (with optimization)"]
E["Complex Lists"]
F["Animations"]
G["Image-Heavy Screens"]
H["Real-time Updates"]
end
subgraph Challenging["Challenging"]
I["3D Graphics"]
J["Video Editing"]
K["AR/VR"]
L["Audio Processing"]
end
style Excellent fill:#e8f5e9,stroke:#388e3c
style Good fill:#fff3e0,stroke:#ff9800
style Challenging fill:#ffebee,stroke:#c62828
Performance characteristics by feature type
Common Performance Myths
❌ Myth: "React Native apps are slow"
Reality: Poorly optimized React Native apps can be slow, just like poorly optimized native apps. Well-built RN apps are fast. Instagram, Discord, and Shopify prove this at massive scale.
❌ Myth: "JavaScript is too slow for mobile"
Reality: JavaScript runs on a highly optimized engine (Hermes, created specifically for React Native). Business logic in JS is plenty fast. Performance-critical code runs natively.
❌ Myth: "Animations will always be janky"
Reality: With React Native Reanimated, animations run at 60fps on the native thread. The days of JS-driven animation jank are over.
Realistic Expectations
✅ What You Can Expect
- Smooth 60fps scrolling with proper list virtualization
- Native-feeling navigation transitions
- Fluid gesture handling
- Fast app startup (especially with Hermes and prebuilt bundles)
- Efficient memory usage
The key insight: performance problems in React Native are almost always solvable. There's usually a right way to do things (memoization, virtualized lists, native-driver animations) that brings performance to native levels. This course will teach you those patterns.
Summary
🎉 Key Takeaways
- There's no DOM, browser, or CSS — React Native runs directly on the device with native UI components
- HTML elements map to native components —
div→View, all text →Text,img→Image - Styles are JavaScript objects — camelCase properties, numeric values, no cascade
- The bridge connects JS and native — understanding this explains async behavior and animation patterns
- iOS and Android are different — but ~85% of code is shared; platform-specific code is manageable
- Performance is not a blocker — with proper patterns, RN apps match native performance for typical use cases
🚀 What's Next?
Now that you understand what's different between web and native, let's flip the script and look at what stays the same. In the next lesson, we'll explore everything that transfers directly from your React knowledge — and it's a lot more than you might think!
🧠 Mental Model: Updated!
You've made the conceptual leap from web to native thinking. The building blocks are different, but the architecture is familiar. You've got this!