Back to blog

React Native: How JavaScript Renders Native UIs

react-nativemobilecross-platformjavascriptreact
React Native: How JavaScript Renders Native UIs

Series: Cross-Platform Mobile Development
Previous: Ionic & Capacitor: How Web Tech Becomes Mobile Apps
Next: Flutter: How Dart Paints Every Pixel


React Native takes a fundamentally different approach from Ionic. Instead of running your app in a WebView, React Native uses JavaScript to control actual native UI components. When you write <View>, it becomes a real UIView on iOS and android.view.View on Android.

No WebView. No HTML. No CSS. Real native components, orchestrated by JavaScript.

This sounds elegant — and it is. But the mechanism that makes this possible has gone through a massive architectural overhaul. Understanding both the old and new architecture reveals why React Native struggled with performance for years and how it finally solved those problems.

The Core Idea

In a web app, the browser renders everything. In React Native, the native platform renders everything:

You write React components, but instead of web elements, you use React Native primitives:

React (Web)React NativeiOS NativeAndroid Native
<div><View>UIViewandroid.view.View
<span><Text>UILabelTextView
<img><Image>UIImageViewImageView
<input><TextInput>UITextFieldEditText
<button><Pressable>UIButtonButton
<ul><FlatList>UITableViewRecyclerView
// This looks like React, but renders native components
import { View, Text, Image, Pressable } from 'react-native';
 
function ProfileCard({ user }) {
  return (
    <View style={styles.card}>
      <Image source={{ uri: user.avatar }} style={styles.avatar} />
      <Text style={styles.name}>{user.name}</Text>
      <Pressable onPress={() => follow(user.id)}>
        <Text>Follow</Text>
      </Pressable>
    </View>
  );
}

This code creates real UIView, UIImageView, UILabel, and UIButton instances on iOS — not web elements inside a WebView.

The Old Architecture (Pre-2022)

Understanding the old architecture explains why React Native had performance issues and why the rewrite was necessary.

Three Threads

React Native ran on three separate threads:

The Bridge Problem

The bridge was the critical bottleneck. All communication between JavaScript and native code went through this bridge:

  1. JavaScript decides to create a <View> with certain styles
  2. The instruction is serialized to JSON and sent across the bridge
  3. The shadow thread receives it, calculates layout with Yoga (a Flexbox engine)
  4. Layout results are serialized to JSON and sent back across the bridge
  5. The UI thread receives the final instructions and creates native views

Every message crossed the bridge as serialized JSON. For simple UIs, this was fine. But for complex interactions:

The symptoms:

  • Scroll jank — touch events had to cross the bridge to JS and back, adding latency
  • Animation stuttering — JS-driven animations sent hundreds of messages per second across the bridge
  • Slow startup — the entire React Native runtime had to initialize before any UI appeared
  • Dropped frames — if the bridge was congested, frames were skipped

This is why React Native got a reputation for "almost native but not quite" performance.

The New Architecture (2022-Present)

React Native's New Architecture is a ground-up rewrite of the communication layer. It eliminates the bridge entirely.

Three Pillars

1. JSI (JavaScript Interface)

The biggest change. JSI replaces the bridge with direct C++ bindings between JavaScript and native code.

Instead of serializing everything to JSON and sending it over an asynchronous bridge, JavaScript can now call C++ functions directly — and C++ can call back into JavaScript directly.

Old Architecture:
JS → serialize to JSON → bridge → deserialize → Native
Native → serialize to JSON → bridge → deserialize → JS
 
New Architecture (JSI):
JS ↔ C++ ↔ Native  (direct function calls, no serialization)

This means:

  • No serialization overhead — objects are shared by reference, not copied
  • Synchronous calls when needed — JS can call native code and get a result immediately
  • Any JS engine — JSI is engine-agnostic (works with Hermes, V8, or JavaScriptCore)

2. Fabric (New Renderer)

Fabric is the new rendering system that replaces the old UI manager. Key improvements:

Concurrent rendering: Fabric integrates with React 18's concurrent features. UI updates can be prioritized — a user tap gets higher priority than a background data refresh.

Synchronous layout: In the old architecture, layout was calculated on a separate thread with async bridge communication. Fabric can compute layout synchronously when needed, eliminating the "layout flash" (where views appeared briefly in wrong positions).

Shared ownership: Both JavaScript and native code can reference the same UI node. In the old architecture, only one side "owned" a view at a time.

3. TurboModules

TurboModules replace the old Native Modules system. Two key improvements:

Lazy loading: Old Native Modules were all initialized at startup, even if never used. TurboModules load only when first accessed — significantly improving startup time.

Type-safe interface: TurboModules use CodeGen to generate C++ interface definitions from TypeScript specs. This means type errors are caught at build time, not at runtime.

// TurboModule spec (TypeScript)
import { TurboModule, TurboModuleRegistry } from 'react-native';
 
export interface Spec extends TurboModule {
  getConstants(): { apiUrl: string };
  fetchUser(id: number): Promise<{ name: string; email: string }>;
}
 
export default TurboModuleRegistry.getEnforcing<Spec>('UserModule');
// Android implementation
public class UserModule extends NativeUserModuleSpec {
    @Override
    public Map<String, Object> getConstants() {
        return Map.of("apiUrl", "https://api.example.com");
    }
 
    @Override
    public void fetchUser(double id, Promise promise) {
        // Native implementation
        promise.resolve(/* user data */);
    }
}

The CodeGen generates a C++ interface that both sides must conform to — catching mismatches at compile time instead of crashing at runtime.

Hermes: React Native's JavaScript Engine

React Native ships with Hermes, a JavaScript engine built specifically for mobile:

Why Not Just Use V8 or JavaScriptCore?

V8 (Chrome's engine) and JavaScriptCore (Safari's engine) are designed for browsers. They optimize for long-running sessions with JIT compilation. Mobile apps have different needs:

  • Fast startup — users expect apps to open instantly
  • Low memory usage — mobile devices have limited RAM
  • Predictable performance — no JIT warmup causing early jank

How Hermes Works

Hermes takes a different approach: Ahead-of-Time (AOT) compilation to bytecode.

Instead of shipping raw JavaScript and compiling it on the device (like V8/JSC), Hermes compiles your code to bytecode at build time. On the device, Hermes just executes pre-compiled bytecode.

Results:

  • ~50% faster startup compared to JavaScriptCore
  • ~30% less memory usage
  • Smaller app size — bytecode is more compact than raw JavaScript
  • No JIT warmup — consistent performance from the first frame

Styling: Not CSS, But Close

React Native doesn't use CSS. Instead, it uses a JavaScript object syntax that resembles CSS:

import { StyleSheet, View, Text } from 'react-native';
 
function Card() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Hello</Text>
    </View>
  );
}
 
const styles = StyleSheet.create({
  container: {
    padding: 16,
    backgroundColor: '#ffffff',
    borderRadius: 8,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3, // Android shadow
  },
  title: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#1a1a1a',
  },
});

Layout uses Flexbox (via the Yoga engine), but with some differences from web CSS:

  • flexDirection defaults to 'column' (not 'row' like web)
  • All dimensions are unitless (no px, em, rem)
  • No cascading — styles don't inherit (except text within <Text>)
  • No media queries (use Dimensions API or useWindowDimensions)

React Native doesn't include a built-in navigation system. React Navigation is the de facto standard:

import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
 
const Stack = createNativeStackNavigator();
 
function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Profile" component={ProfileScreen} />
        <Stack.Screen name="Settings" component={SettingsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

With @react-navigation/native-stack, navigation animations and gestures are handled by the native navigation controller (UINavigationController on iOS, Fragment on Android) — not JavaScript-driven animations. This gives truly native-feeling navigation.

When React Native Is the Right Choice

Perfect Fit ✅

  • Your team already knows React — the learning curve is minimal
  • You need native look-and-feel — native components means native behavior
  • You want to share logic with a React web app — business logic, state management, API layers can be shared
  • Complex apps with platform-specific UX — React Native makes it easy to customize per platform
  • Apps with real-time interactions — chat, social feeds, collaborative editing

Think Twice ⚠️

  • Your team doesn't know React — the React paradigm (hooks, state, effects) has a learning curve
  • You need pixel-perfect custom UI — React Native components look native, which means they look different on iOS vs Android
  • Heavy WebView usage — if your app is mostly web content, Ionic/Capacitor is simpler
  • Games or graphics-intensive apps — use Unity, Godot, or Flutter
  • Apps with minimal native API needs — if you just need a camera and push notifications, Ionic is simpler
  • Your existing app is in Angular/Vue — React Native requires React knowledge

The Ecosystem in 2026

React Native's ecosystem is massive and battle-tested:

  • Expo — the most popular way to build React Native apps. Managed workflow, EAS Build, OTA updates, and a rich SDK. Many teams never need to eject.
  • React Navigation — native navigation with stack, tab, and drawer navigators
  • Reanimated 3 — declarative animations that run on the UI thread (no bridge crossing)
  • React Native MMKV — ultra-fast key-value storage
  • React Native Skia — Skia rendering engine in React Native (for custom drawing)
  • Expo Router — file-based routing (like Next.js for mobile)

Who Uses React Native?

Major apps running React Native in production:

  • Meta — Facebook, Instagram, Messenger, Ads Manager
  • Microsoft — Xbox app, Office, Teams, Outlook
  • Shopify — entire mobile app
  • Discord — iOS and Android
  • Bloomberg — financial terminal mobile app
  • Coinbase — cryptocurrency trading
  • Flipkart — India's largest e-commerce app

These aren't toy apps. They're billion-dollar products serving hundreds of millions of users.

Key Takeaways

  1. React Native renders real native components, not web elements — <View> becomes UIView/android.view.View
  2. The old bridge architecture (JSON serialization) caused performance bottlenecks — scroll jank, animation stutter, slow startup
  3. The New Architecture (JSI + Fabric + TurboModules) eliminates the bridge with direct C++ bindings — a transformative improvement
  4. Hermes engine provides fast startup and low memory usage through ahead-of-time bytecode compilation
  5. The ecosystem is mature — Expo, React Navigation, Reanimated make complex apps achievable
  6. If you know React, you're 70% of the way to building React Native apps — the paradigm transfers directly

React Native's New Architecture was a bet that the "JavaScript controlling native components" model could work at scale. The results — shipped by Meta, Microsoft, Shopify, and Discord — prove that bet paid off.


Series: Cross-Platform Mobile Development
Previous: Ionic & Capacitor: How Web Tech Becomes Mobile Apps
Next: Flutter: How Dart Paints Every Pixel

📬 Subscribe to Newsletter

Get the latest blog posts delivered to your inbox every week. No spam, unsubscribe anytime.

We respect your privacy. Unsubscribe at any time.

💬 Comments

Sign in to leave a comment

We'll never post without your permission.