No history yet

JSI Architecture

Beyond the Bridge

For years, React Native's architecture was defined by its asynchronous bridge. This bridge connected the JavaScript thread, where the application logic runs, to the native UI and main threads. Communication happened by serializing messages into JSON strings, sending them across the divide, and then deserializing them on the other side. This process, while functional, introduced inherent latency and a performance bottleneck often called the 'JSON tax'. Every interaction, from a simple button tap to a complex animation update, had to pay this tax, leading to potential UI stutters and a feeling of disconnect from the native platform.

The core limitation was its asynchronous and batched nature. You couldn't directly and synchronously call a native method from JavaScript or vice-versa. This made certain tasks, especially those requiring high-frequency updates like animations, difficult to implement performantly. The entire system was fundamentally disconnected, operating in three distinct worlds: the JS thread, the native main thread, and the shadow thread for layout.

In the old React Native model, your app’s business logic was written in JavaScript, while UI and platform APIs (buttons, navigation, camera, etc.) lived in native code (Objective-C/Swift for iOS, Java/Kotlin for Android).

The JSI Revolution

The New Architecture replaces the bridge with a new mechanism: the JavaScript Interface, or JSI. JSI isn't a replacement for the bridge; it's a completely different paradigm. It is a lightweight, general-purpose C++ API that allows the JavaScript engine itself to directly communicate with native code. Instead of serializing messages, the JavaScript engine can now hold a direct reference to a C++ object and invoke its methods.

This is made possible through C++ Host Objects, which are C++ classes whose instances can be passed into the JavaScript runtime. Once inside the JS context, they behave like standard JavaScript objects. You can call their methods and access their properties, but these operations execute C++ code directly and synchronously. This eliminates the serialization overhead and the asynchronous queue, enabling immediate communication between the two realms.

// Simplified example of a C++ Host Object
class MyHostObject : public jsi::HostObject {
public:
  // Expose methods to JavaScript
  jsi::Value get(jsi::Runtime& rt, const jsi::PropNameID& name) override {
    auto propName = name.utf8(rt);
    if (propName == "doSomethingSync") {
      return jsi::Function::createFromHostFunction(
        rt,
        name,
        0, // argument count
        [this](jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count) -> jsi::Value {
          // This is a direct, synchronous call into C++
          this->nativeMethod();
          return jsi::Value::undefined();
        }
      );
    }
    return jsi::Value::undefined();
  }

  void nativeMethod() {
    // Native C++ logic runs here instantly
  }
};

The was designed with JSI in mind. Its architecture is optimized for low memory usage and fast startup, but crucially, it's built to facilitate this kind of direct interoperability. The engine can efficiently manage references to these C++ Host Objects, making the integration feel seamless from a developer's perspective.

Performance and Memory Safety

The performance implications are significant. Libraries like Reanimated and Skia use JSI to achieve 60 FPS animations and complex graphical manipulations that run on the UI thread, completely bypassing the JS thread. Since they can execute native code synchronously, they avoid the latency of the old bridge, resulting in a truly native feel.

With JSI, a JavaScript call to a native module can be as fast as an inline function call in C++.

This direct access raises questions about memory management. If JavaScript holds a reference to a C++ object, who is responsible for deleting it? JSI solves this using a shared ownership model based on C++ smart pointers, specifically std::shared_ptr. When a Host Object is created, it's wrapped in a shared pointer. Every time a reference to this object is held, either in C++ or by the JavaScript runtime, the reference count is incremented. When the reference is released (e.g., the JS object is garbage collected), the count is decremented. The C++ object is only destroyed when the reference count hits zero, ensuring memory safety across both environments.

This also revolutionizes the threading model. The rigid three-thread system is gone. JSI is thread-agnostic. It allows JavaScript to get a reference to a C++ function and invoke it on any thread, giving developers unprecedented control over concurrency and performance optimization.

Time to check your understanding of the JSI architecture.

Quiz Questions 1/5

What was the primary performance bottleneck in React Native's old bridge architecture, often referred to as the 'JSON tax'?

Quiz Questions 2/5

How does the JavaScript Interface (JSI) enable synchronous communication between JavaScript and native code?

By enabling direct, synchronous communication, JSI fundamentally changes what's possible in React Native, paving the way for more performant and complex applications.