Heyo dev! 👋
Below is a lil bit extended version of the answer I gave to a SO question I came across recently. I decided to spread it here too incase anyone is still stuck with the same question or just want to get an overview of how a React Native app is architected and work internally.
I'm just going to try to explain the roles of the three threads used in a React Native app, how they work together, and how JavaScript is handled for a React Native app in debug/dev mode and in prod/release mode.
In Dev / Debug mode
Metro bundler (which runs on top of Node.js) bundles all your JavaScript codes: The ones you wrote, the ones in your app's dependency packages, and the ones that comes with React Native. It bundles them into one big JS file, then spins up a local server on port 8081
to serve the bundled file when requested.
Now when you open your compiled debug app (.apk
for Android or .ipa
for iOS), it makes a request to the local metro server to fetch the bundled JS file so that the JS interpreter (JavaScriptCore or Hermes which is packaged into your app can begin executing it. But you may ask, what happens to the native codes (Java/Kotlin
or Obj-C/Swift
) that is also part of the compiled app?
The simple answer is, these native codes cannot be executed by the JS interpreter, so it has to be executed separately but within the same app. That means JS has to be executed separately and native codes also has to be executed separately, but all within the same app because the two has to come together to make your app whole.
Fortunately, Threading allows for such things, they allow you to run multiple sub-programs (threads if you will) within the main program (process). With threading, you can execute two or more programs which can be written in different programming languages concurrently within the same process.
So when your compiled React Native app is launched (as a process), what actually happens is it spins up three threads from within it:
- JS thread
This is where the JavaScript interpreter which executes your bundled JavaScript - sent from metro server - lives
- Shadow thread
This is where the flexbox-based styles and layout you give to your elements are converted into layout and styles that the native platform (Android or iOS) can understand. This is done by Yoga, a cross-platform layout engine.
- Main/UI/Native thread
This is where all the native Java/Kotlin
or Obj-C/Swift
codes are executed. It is here that native platform APIs (e.g Camera) are accessed from and native platform UI elements (e.g View
for Android and UIView
for iOS) are rendered. It is from this thread that the other two threads are spawn.
The communication between the three threads mentioned above are facilitated by the React Native Bridge or Turbo Native Modules which is the new approach.
In Prod / Release mode (when you compile for release to app stores)
Everything is the same as in Dev/Debug mode except that the bundled JavaScript file is packaged together with your app (apk
or ipa
) as an asset when the app is being compiled, rather than being loaded from metro server.
So when you open the compiled app, the JS bundle is just loaded to the JS thread from within the app's assets folder and is executed. That also means there's no need for Metro or Node.js from that point as everything the app needs to work is already precompiled into it.
That's it,
Hope this helped at least a bit ✌
Top comments (0)