When I started learning React Native, I often saw tutorials that said “React Native lets you build apps for Android and iOS using JavaScript.”
Cool, but how does it actually work under the hood?
What really happens when I write this:
<Text>Hello</Text>
<Button title="Tap" onPress={() => {}} />
and then see “Hello” and a button show up on my phone screen?
So, I spent time breaking it down with the help of AI — and here’s the full explanation, written in simple human English, step-by-step.
This will help you understand what’s actually happening behind your React Native app — clearly and completely.
🚀 What Actually Happens When You Open the Homepage in React Native
Let’s say your main file is:
/app/index.tsx
<Text>Hello</Text>
<Button title="Tap" onPress={() => {}} />
Let’s peel the entire process — from running the app to seeing real UI and handling taps.
✅ Step 1: You Run the App
You type:
npx expo start
Then press a
→ the Android emulator opens.
📱 The emulator is just a virtual Android phone running on your computer.
✅ Step 2: Expo Bundles and Sends Your Code
- Expo takes all your
.tsx
and.js
files, bundles them into one big optimized JavaScript file, - and sends that file to the emulator.
- Inside the emulator / device , there’s a JavaScript engine (called Hermes like a mini-browser brain) that executes this bundled file means it runs your code.
🧠 Think of Hermes as the brain that understands and runs your JavaScript inside the app.
✅ Step 3: React Builds a “Virtual Plan”
Your code says:
“Show text ‘Hello’ and a button.”
React (which lives in the JavaScript engine) doesn’t draw anything yet , it makes an to-do-list.
It first creates a virtual plan in memory — the React element tree:
- Create a text that says “Hello”
- Create a button that says “Tap”
- Put them on the screen
{
type: 'RCTText',
props: { children: 'Hello' }
},
{
type: 'RCTButton',
props: { title: 'Tap', onPress: () => {} }
}
This is the blueprint of your UI — still in JavaScript memory, not on screen yet.
✅ Step 4: React Native Acts as the Translator 🗣️
Here’s where the magic begins.
Android doesn’t understand JavaScript — it understands Java/Kotlin.
So React Native acts as a translator between these two worlds.
It takes React’s virtual plan and says to Android:
“Hey Android! Please create a native view of type
RCTText
with text ‘Hello’, and a native button below it.”
This message travels through a direct connection called JSI (JavaScript Interface) — like a walkie-talkie between JavaScript and Android.
JSI (JavaScript Interface) a super-fast C++ bridge that lets JavaScript talk directly to native code.
✅ Step 5: Android Creates Real Native Views
Android receives those messages.
It has a registry mapping React Native component names to Android classes:
React Native Name | Android Class |
---|---|
RCTText |
ReactTextView → extends TextView
|
RCTView |
ReactViewGroup → extends ViewGroup
|
RCTButton |
ReactButton → uses AppCompatButton
|
The Android system (in the emulator) hears this and:
- Creates a real TextView (Android’s native text component)
- Creates a real Button (Android’s native button)
- Puts them on the screen
“Create
RCTText
with text ‘Hello’”
It runs Java code roughly like:
ReactTextView textView = new ReactTextView(context);
textView.setText("Hello");
parent.addView(textView);
✅ Now you see real Android UI — actual TextView
and Button
objects drawn by the system.
They are not HTML or web views — they’re real native components.
✅ Step 6: The UI Appears on Screen
Here’s a simplified diagram:
[Your Code: index.tsx]
↓
[React builds virtual plan]
↓
[React Native sends plan via JSI]
↓
[Android creates real native views]
↓
✅ “Hello” and a button appear on emulator
So what you see are actual Android-native UI components, controlled by your JavaScript.
✅ Step 7: Interaction — You Tap the Button
You tap the button on the emulator.
-
Android feels the tap and says:
“Hey JavaScript! The user tapped the button.”
- It sends a message back through the same walkie-talkie (JSI).
Your JavaScript function runs:
() => console.log("Tapped!");
If the function updates state, the cycle repeats ,React creates a new virtual plan, compares it to the old one, and updates only what changed — fast and efficient.
🧩 Where the Mapping Lives
In React Native’s source (already bundled in Expo):
ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java
That file registers RCTText
and defines how Android should create and update it.
You never touch this — Expo handles it automatically.
You just write <Text>
in JS, and the translator handles everything.
🔍 Want to See the Native Views?
Open Android Studio → Layout Inspector while the app runs.
You’ll see something like:
ReactRootView
└── ReactViewGroup
└── ReactTextView ← your <Text>
└── ReactButton ← your <Button>
✅ Proof that these are real Android views created by React Native’s translator.
🧱 What You Don’t Need to Worry About (Yet)
JS Thread vs Native Thread — Expo handles the sync.
Bridge vs JSI — You’re already using the new fast one (JSI).
/android
folder — ignore it in Expo.Yoga Layout — it just makes Flexbox (
flex: 1
) work.
⚡ In One Sentence
You write
<Text>Hello</Text>
in JavaScript → React builds a virtual plan → React Native (via JSI) tells Android → Android builds a realTextView
→ You see and interact with it → Events flow back → React updates again.
That’s the entire React Native lifecycle —
no magic, just smart translation between JS and Native worlds.
❓ React Native Deep-Dive — Q&A Section
A collection of simple, clear answers to the most common “how does it really work” questions.
🧩 Q1: Where is the mapping between <Text>
and Android’s TextView
defined?
In React Native, <Text>
in JavaScript doesn’t directly create Android UI. Internally, React Native uses the name RCTText
as a bridge.
The mapping is part of React Native’s Android source code (shipped with the framework), specifically in:
ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java
🔍 What happens there:
"RCTText"
is registered as the internal name React Native uses.createViewInstance()
creates a ReactTextView, which wraps Android’s TextView.Props like
text
,color
, andfontSize
are automatically handled from JS.
✅ So when you write <Text>
, React Native uses this mapping to create a real native TextView
on Android.
You don’t need to touch this — it’s already part of the framework.
🚫 Q2: What happens if I create a component that doesn’t exist on the native side?
If you write a component in JavaScript that has no corresponding native module, React Native cannot translate it into a real UI element.
At runtime, React Native will throw an error like:
Invariant Violation: Component not found
🔧 Why it happens:
Core components like
<Text>
,<View>
, and<Button>
work because React Native already provides native implementations for them.For custom components, you can either:
1. Build them using existing core components (pure JS), or
2. Create a **custom native module** for Android/iOS.
✅ TL;DR: React Native can only render components that have a native mapping. If it doesn’t exist, it fails with an error.
⚙️ Q3: Where does JSI actually stay — in my project folder, in Hermes, or in the device?
JSI (JavaScript Interface) is part of React Native’s native code compiled into the app, not in your project files.
It lives inside the app on the device.
It acts as a bridge connecting:
the JS thread (where Hermes runs your code)
and the native/UI thread (where the OS draws your views).
Through JSI, your JavaScript can call native functions directly and efficiently.
✅ TL;DR: JSI lives inside the app binary on the device, linking JS (Hermes) to native code — it’s not in your /project
folder or inside Hermes itself.
🧵 Q4: Are the JS thread and native thread part of the bundle or something else?
No — they are not part of your JS bundle.
🧠 Breakdown:
JS Thread: Created by the Hermes engine at runtime on the device. It runs your JavaScript code.
Native Thread (UI Thread): Created by the OS (Android/iOS). It handles rendering and user interactions.
Your bundle is just your JavaScript code — it runs on the JS thread, which communicates with the native thread via JSI.
✅ TL;DR: Threads exist on the device at runtime, not inside your JS bundle. The bundle just runs on the JS thread.
🧩 Q5: Do the native thread and JSI live inside the project’s /android
folder or in Hermes?
Neither.
Native Thread: Created by the OS on the device to handle UI and system tasks.
JSI (JavaScript Interface): Part of React Native’s compiled native code, included in the app binary.
Hermes: Only runs your JavaScript code on the JS thread.
JSI connects Hermes (JS) to the native thread, allowing them to talk.
✅ TL;DR: Both JSI and the native thread exist on the device at runtime — not in your project folder or inside Hermes.
💡 Q6: What’s the difference between code in my project (/app
, /android
) and what really runs on the device?
Your project files (/app
, /android
, etc.) contain source code — the code you write in JS, XML, Java, or Kotlin.
What runs on the device is the compiled app binary, which includes:
Your JS bundle, executed by Hermes on the JS thread.
Your native code, compiled for Android/iOS, running on the native thread.
React Native bridges them via JSI, so your JS can control real native UI components.
✅ TL;DR: Source code is your “instructions.” The device runs the compiled JS and native code, connected through JSI.
📦 Q7: When does bundling happen, and how does Hermes get my code?
Bundling happens when you run or build your app — for example, with:
npx expo start
or during a production build.
All your JavaScript files are combined into a single JS bundle, which is then:
Sent to the device/emulator,
Executed by Hermes on the JS thread.
Hermes runs your JS code and communicates with the native world via JSI.
✅ TL;DR: Bundling packages your JS into one file, which Hermes executes at runtime — JSI connects it to native code.
Top comments (0)