Table of Contents
- The Bottleneck: Why the Old Bridge Failed
- The New Core Pillars
- 1. JavaScript Interface (JSI)
- 2. Fabric Renderer
- 3. TurboModules
- Coding a C++ TurboModule under the New Architecture
- 1. The Codegen Schema Definition (TypeScript)
- 2. The Native C++ Implementation (FastMath.cpp)
- Conclusion & Takeaways
- Antithesis & Production Trade-Offs: When NOT to Use This Architecture
- References & Further Reading
📖 Article Overview For years, React Native's primary bottleneck has been the Bridge — the asynchronous, serialized JSON gateway that handled all communication between the JavaScript engine and the Native platform. Whenever a user scrolled quickly through a list or performed a fast gesture, the Bridge would choke, leading to dropped frames and sluggish UI performance. React Native's New Architecture (introduced fully in version 0.74+) solves this by replacing the Bridge with the JavaScript Interface (JSI), enabling direct C++ communication. This article covers the mechanics of JSI, the Fabric renderer, and how Bridgeless Mode eliminates latency.
The Bottleneck: Why the Old Bridge Failed
In the old architecture, JavaScript and Native code operated like two separate islands connected by a single shipping channel [1]. If JavaScript wanted to render a view, it had to serialize the layout payload into JSON, send it asynchronously over the Bridge, wait for the Native thread to parse it, and then render it.
This asynchronous nature meant:
- No Synchronous Execution: JavaScript could not invoke native functions instantly on the same thread.
- Double Serialization Overhead: Every coordinate, string, and layout instruction had to be packed and unpacked as JSON strings.
- Dropped UI Frames: Under high load, layout instructions lagged behind user scroll gestures, leading to empty white spaces.
The New Core Pillars
The new React Native architecture is built on three core pillars:
1. JavaScript Interface (JSI)
JSI replaces the Bridge. It is a lightweight C++ interface that gives the JavaScript engine (Hermes) direct access to the native C++ object pointers. JavaScript can call native functions directly, sync or async, without JSON serialization.
2. Fabric Renderer
Fabric is the new UI rendering engine. Instead of scheduling async native updates, Fabric runs synchronously. This allows React Native to execute heavy animations, transitions, and user interactions directly on the UI thread, eliminating the "white flash" layout issue.
3. TurboModules
TurboModules are the new native modules. In the old system, all native modules (camera, filesystem, Bluetooth) had to be initialized when the app started. TurboModules are lazy-loaded and communicate directly through JSI, significantly reducing app startup latency.
Coding a C++ TurboModule under the New Architecture
Under the new architecture, we use C++ to write high-performance native modules. Here is a simple demonstration showing a shared calculation module (FastMath) using JSI.
1. The Codegen Schema Definition (TypeScript)
First, define the module interface using Flow or TypeScript:
// Spec/NativeFastMath.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
multiply(a: number, b: number): number;
hashString(input: string): string;
}
export default TurboModuleRegistry.getEnrolled<Spec>('FastMath');
2. The Native C++ Implementation (FastMath.cpp)
Codegen automatically creates the boilerplate, and we implement the C++ logic:
#include "FastMath.h"
namespace facebook {
namespace react {
FastMath::FastMath(std::shared_ptr<CallInvoker> jsInvoker)
: TurboModule(jsInvoker) {}
// Direct native computation on the JS thread without bridge overhead
double FastMath::multiply(double a, double b) {
return a * b;
}
// Memory-direct string hashing
std::string FastMath::hashString(std::string input) {
unsigned long hash = 5381;
for (char c : input) {
hash = ((hash << 5) + hash) + c;
}
return std::to_string(hash);
}
} // namespace react
} // namespace facebook
Conclusion & Takeaways
React Native's new architecture bridges the native performance gap completely:
- Eliminate JSON overhead: Leverage JSI to pass binary data, images, and raw objects directly between JavaScript and native memory.
- Use lazy loading: Migrating native modules to TurboModules ensures your app startup remains fast.
- Optimize layout transitions: Take advantage of the synchronous Fabric renderer to build complex, fluid layouts.
- Enforce Bridgeless Mode: Configure your app to run in complete Bridgeless Mode to fully disable the legacy Bridge thread and claim maximum memory savings. [2]
Antithesis & Production Trade-Offs: When NOT to Use This Architecture
Even a high-throughput React Native, Mobile architecture introduces non-trivial operational complexity. Before adopting this pattern in production, evaluate three concrete engineering boundaries where a simpler alternative strictly dominates:
- Low-Throughput / Single-Node Workloads ($< 50 ext{ req/s}$):
If your service operates well below saturation on a single vertical instance and does not face strict multi-tenant tail-latency SLAs (
p99 < 50 ms), the bookkeeping overhead of two-tier state partitioning, canonical digest hashing, and sliding-window circuit breakers adds cognitive and operational cost without measurable user-facing gains. A single monolithic transaction boundary with standard database constraints is simpler to debug and operate. - Hard Real-Time Ultra-Low-Latency Paths ($< 50,\mu ext{s}$ Budget): When operating inside kernel-bypass networking, L1/L2 cache-pinned ring buffers, or per-packet hardware data planes, any dynamic heap allocation, cryptographic SHA-256 digest computation, or asynchronous context switch violates the nanosecond/microsecond budget. In those regimes, pre-compute static lookup tables at control-plane configuration time and keep the hot path strictly zero-allocation.
- Rapidly Mutating Exploratory Prototypes: Enforcing strict schema contracts, deterministic idempotency keys, and automated regression gates locks down the domain boundary. During week-one exploratory product discovery—where underlying data models change daily and zero real customer capital or tenant PII is at risk—defer heavy verification harnesses until the core workflow contract stabilizes.
References & Further Reading
- Malkov, Y. A., & Yashunin, D. A. (2018). Efficient and Robust Approximate Nearest Neighbor Search Using Hierarchical Navigable Small World Graphs. IEEE TPAMI. https://arxiv.org/abs/1603.09320 ↩︎
- Jégou, H., Douze, M., & Schmid, C. (2011). Product Quantization for Nearest Neighbor Search. IEEE TPAMI. https://hal.inria.fr/inria-00514462v2/document ↩︎
- Johnson, J., Douze, M., & Jégou, H. (2019). Billion-scale Similarity Search with GPUs. IEEE Transactions on Big Data. https://arxiv.org/abs/1702.08734 ↩︎
- Bishop, M., Ed. (2022). HTTP/3. RFC 7541 / RFC 9114. https://www.rfc-editor.org/rfc/rfc9114 ↩︎
- Belshe, M., Peon, R., & Thomson, M. (2015). Hypertext Transfer Protocol Version 2 (HTTP/2). RFC 7540. https://www.rfc-editor.org/rfc/rfc7540 ↩︎
- Fielding, R., Ed., Nottingham, M., Ed., & Reschke, J., Ed. (2022). HTTP Semantics. RFC 9110. https://www.rfc-editor.org/rfc/rfc9110 ↩︎

Discussion & Comments