What does React Native use to allow JavaScript to be executed on iOS and Android natively?

As you noticed React Native is not based on Cordova. It is not a website which looks like an app shoveled into a WebView.

React Native uses a JavaScript runtime, but the UI is not HTML and it doesn't use a WebView. You use JSX and React Native specific components to define the UI.

It provides a native-level performance and look and feel but some UI parts have to be configured separately for iOS and Android. For example Toolbars are completely different, but TextInput can be the same for both Operating Systems.


For new update refer @f4z3k4s answer.

React Native app comprises two parts: Native part and Javascript part.

To run Javascript

React Native uses JavaScriptCore (JavaScript engine in Safari) on Android/ iOS simulators and devices.

In case of Android, React Native bundles the JavaScriptCore along with the application.

In case of iOS, React Native uses the JavaScriptCore provided by the iOS platform.

To communicate Javascript with Native part

React Native bridge is used. It is a layer that is responsible for communication between the Native and Javascript thread. It is written in C++/Java.

Steps Involved

After developer runs react-native run-ios or react-native run-android

  1. React Native CLI spawns a node packager/bundler (metro) that would bundle the javascript code into a single main.bundle.js file.
  2. React Native CLI launches React Native app and at first loads the native entry point of the app.
  3. The Native thread spawns the JavascriptCore thread.
  4. The Native thread now sends messages via the RN Bridge to start the Javascript app.
  5. Javascript thread starts issuing instructions to the Native thread via the RN Bridge. The instructions include what views to load, what information is to be retrieved from the hardware, etc.
  6. The Native thread will perform these operations and send the result back to the Javascript thread assuring that the operations have been performed.

Source


On iOS simulators and devices, Android emulators and devices React Native uses JavaScriptCore which is the JavaScript engine that powers Safari. Source


2022 Update

As React Native has changed a lot in recent times and will change a lot in the near future, I thought it's timely to update this answer:

New components to understand:

Hermes:

React Native team developed their own Javascript Engine in C++, called Hermes. If enabled, Hermes will be shipped with your app as a context for executing Javascript (instead of JSC), promising to make your app's bundle smaller, and your app's execution faster.

JSI (Javascript Interface):

It is a unified, lightweight, general-purpose layer for (theoretically) any JavaScript engine. It's implemented in C++, just as the engine, but decoupled from the engine. This makes it possible to hold references of native objects on the JS thread, eliminating the need for the bridge, thus eliminating the need to stringify everything and making React Native way faster altogether as the stringification is the bottleneck currently. It also makes it easier to switch between JS engines. As JSI is developed in C++, it makes it easier for developers to share native code between Android and iOS. For example, you can develop native code in C++, call it both on iOS and Android from Javascript through the help of JSI. Both platforms can execute C++ code, iOS can use it really easily through Objective C as it is a superset of C. Android needs a bit more work with the help of Android NDK (Native Development Kit) and JNI (Java Native Interface, responsible for Java <=> C++ translation).

Bridge will be split into two parts, read more:

Turbo modules

This is a new way of interacting with the native side, interoperable with JSI. It allows lazy initialization of packages, thus making the startup time of apps having lots of native dependencies way shorter.

Fabric renderer

Re-architecture of the UI manager, that is interoperable with JSI and Turbo modules. Instead of the Shadow Tree (or Shadow Thread) that was formerly used for calculation the layout of elements (it was coupled with the bridge), Fabric makes it possible to use the new powerful JSI features to render the ui without having to stringify anything. This will deliver truly native-like performance.

Extras:

Code generation

By using the typed JavaScript (Typescript or Flow) as the source of truth, we can generate interface files needed by Fabric and TurboModules.

New answer for the original question:

React Native uses React on the Javascript side to allow developers to develop applications. Instead of HTML, that's React's rendering context on the web, React Native uses Fabric as a renderer, which is a C++ renderer, which in the end, renders truly native elements on the UI thread (native) of your React Native application. As opposed to the previous model, that used a bridge and stringification between the UI thread and the JS thread to pass data between the two, a new adapter layer, called JSI is implemented, which makes it possible to hold references of C++ objects in JS. Thus, the source of truth for React Native applications will be held in C++ world, allowing both the native and the JS side to share data between the two without any need for stringifying anything, in a synchronous fashion.