Skip to main content

🧠 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 getElementById
  • window — No window object, no localStorage (use AsyncStorage instead)
  • CSS files — No importing .css files, no CSS classes
  • HTML tags — No div, span, p, img, input, etc.
  • CSS cascade — Styles don't inherit the way they do in CSS
  • Browser 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) Browser (Chrome, Safari, Firefox) DOM (Document Object Model) HTML CSS JavaScript (React) 📱 Native (React Native) No Browser (runs directly on device) Native UI (UIKit / Android Views) View, Text, etc. StyleSheet JavaScript (React Native)

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 Design Language: Human Interface Guidelines Navigation: • Back gesture (swipe from left edge) • Tab bar at bottom • Large titles that collapse on scroll Visual Style: • Shadows (via shadowColor, etc.) • Blur effects common • SF Pro font family Safe Areas: • Notch, Dynamic Island, home indicator 🤖 Android Design Language: Material Design 3 Navigation: • Hardware/gesture back button • Bottom nav or navigation drawer • App bar with optional tabs Visual Style: • Elevation (via elevation prop) • Ripple effects on touch • Roboto font family Safe Areas: • Status bar, navigation bar, notches vary

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 componentsdivView, all text → Text, imgImage
  • 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!