Quick Answer

Quick Answer: Classic React Native ran your JavaScript and the native Android/iOS code as two separate worlds connected only by an asynchronous queue of serialized JSON messages called the “bridge.” That was fine for occasional calls but slow for anything frequent, like scroll position or gesture tracking. The New Architecture replaces it with JSI (JavaScript Interface), a thin C++ layer that lets JavaScript call native functions directly and synchronously — no JSON, no queue — and this is the default setup since React Native 0.76.

Why React Native Needs a Bridge At All

A React Native app is really two separate programs running at once. One is your JavaScript code, executing inside a JavaScript engine (historically JSC, more commonly Hermes today) on its own thread. The other is the actual native UI — real UIViews on iOS, real Views on Android — managed on the platform’s own main/UI thread, the same thread a fully native app would use.

These two worlds speak different languages and live in different memory spaces, by design — that separation is exactly what lets React Native update its JavaScript bundle over-the-air without touching native code, and what lets your component code stay platform-agnostic. But it creates an obvious question: when your JS calls Text or fires a button’s onPress, how does that instruction (and the native UI’s response) cross from one world to the other?

Classic React Native’s answer was “the bridge”: a single, well-defined channel both sides agreed to communicate through, where every message was a plain, serializable, asynchronous packet. It worked, and it shipped a huge number of real apps for close to a decade. The problems it had were not bugs — they were consequences of the design choices that made it work at all.

It helps to picture it concretely: your JS thread and the native UI thread are like two people who can only pass notes through a narrow mail slot, never speak directly. Every note has to be written out in full (serialized), pushed through the slot, and read on the other side (deserialized) before anything happens. For a note that says “show an alert when the button is tapped,” that is completely fine — taps are infrequent and nobody notices a few milliseconds of note-passing. The trouble starts when you need to pass hundreds of notes a second, which is exactly what a smooth 60fps scroll or drag gesture requires.

The Old Bridge, In Detail

When JavaScript called a native method — say, asking the native layer to show a native alert — the call did not happen immediately. It got turned into a JSON-serializable message (module id, method id, arguments), pushed onto a queue, and the queue was flushed in batches, typically once per frame. The native side picked up the batch, deserialized each message, executed the real native call, and — if a return value was needed — serialized a response back across the same kind of queue in the other direction.

Three consequences fall directly out of that design:

  • Everything was asynchronous, even calls that conceptually should have been instant, like reading a view’s current measured size. You always got a callback or a Promise, never the value right there in the same line.
  • Everything was serialized to and from JSON-like structures. For a button tap, that overhead is irrelevant. For something firing dozens of times a second — a scroll handler driving a parallax header, or a gesture library tracking a finger drag — that serialization cost adds up into visible, frame-dropping jank.
  • Native modules had to be registered and often loaded eagerly, which added to app startup time as the app grew, since the bridge needed to know about every module it might be asked to call.

None of this made classic React Native “slow” in general — plenty of production apps ran smoothly on it for years — but it put a hard ceiling on how fluid certain interaction patterns could feel, and it meant native-feeling scroll and gesture work usually needed special-cased native modules to get around the bridge’s async nature.

What JSI Actually Is

JSI stands for JavaScript Interface. According to React Native’s own documentation, it is described as a lightweight API for embedding a JavaScript engine inside a C++ application — which is exactly the situation React Native is in: a JS engine (Hermes, JSC, or in principle any engine that implements the JSI contract) needs to talk to a C++/native host app.

The practical difference from the old bridge is that JSI lets JavaScript hold a direct reference to a native C++ object (through something called a HostObject) and call methods on it synchronously, in the same call stack, with no serialization step and no queue in between. If a native function returns a value, JavaScript gets that value back immediately, the same way calling a regular JS function does. There is no JSON anywhere in this path — the whole reason it is faster is that there is nothing to encode or decode.

This single change is also what makes it possible for JavaScript and native code to share memory directly in some cases, instead of always copying data across a boundary, and it is why libraries that need tight native integration — fast image processing, custom native UI components, low-level sensor access — benefit the most from the switch.

A useful way to remember the distinction: the old bridge made every cross-boundary call feel like an API request over a network — you send a message and wait for a response, even if that response technically comes back a few milliseconds later. JSI makes a cross-boundary call feel like calling a function in the same program, because from the CPU’s perspective, that is now much closer to what is actually happening. This is also why JSI needed to be built as a C++ interface rather than something simpler: both the JavaScript engine and the native Android/iOS layers are already written in or callable from C++, so putting the interface at that layer avoids yet another translation step.

TurboModules and Fabric: JSI Applied to Modules and the UI

JSI by itself is just the communication layer — two other pieces of the New Architecture are built on top of it to actually replace what the old bridge did:

TurboModules are the New Architecture’s replacement for native modules. Instead of every native module being instantiated and registered at app startup (which the old bridge required, since it needed to know everything it might be asked to route a message to), TurboModules are loaded lazily, the first time JavaScript actually references them, and calls to them go directly through JSI instead of through the message queue. This both speeds up app startup and removes the serialization cost per call.

Fabric is the New Architecture’s replacement rendering system for the actual UI tree. It uses JSI to let JavaScript and the native rendering layer share the same underlying tree representation more directly, which makes operations like synchronously measuring a view’s layout — something that used to require an async round trip across the bridge — possible to do in the same call. This is a big part of why gesture-heavy interactions and custom scroll behaviour feel noticeably smoother on the New Architecture.

Together, JSI plus TurboModules plus Fabric are what people mean when they say “the New Architecture,” and as of React Native 0.76, this is the default setup for every new project, not an opt-in experiment.

It helps to keep the division of labour straight: JSI is the plumbing (how a call physically crosses from JS to native and back), TurboModules is what flows through that plumbing for native functionality that is not strictly about drawing pixels (a camera API, local storage, a Bluetooth sensor), and Fabric is what flows through it specifically for building and updating the on-screen UI tree. A library you add to your project will usually describe itself as “New Architecture compatible” as a single label covering both, but when you are debugging a specific slow interaction, knowing whether the bottleneck is a TurboModule call or a Fabric layout pass tells you which part of the stack to actually look at.

Bridgeless Mode: Removing the Bridge Entirely

React Native 0.74 shipped Bridgeless Mode as the default under the New Architecture — the literal removal of the old bridge object, with JSI handling 100% of JS-to-native communication instead of the bridge handling some paths and JSI handling others. React Native 0.76 then made the whole New Architecture (bridgeless included) the default for new apps, which means a brand-new React Native project created today never instantiates the old bridge at all.

If you are picking up an existing React Native codebase, you may still encounter the term “bridge” in code, comments, or third-party library docs — it stuck around as the colloquial name for “how JS and native talk to each other” even after the literal bridge object was removed, the same way developers still say “I’ll bridge this native module” to mean “I’ll expose this native functionality to JS,” regardless of which mechanism underneath actually does it.

For apps migrating from an older React Native version, the framework ships an interoperability layer specifically so that libraries which have not yet been updated for the New Architecture can still mostly work, though the React Native team’s own guidance is that the most popular community libraries have already added New Architecture support, so for most apps the upgrade is comparable in effort to any other React Native version bump rather than a rewrite.

It is worth being precise about the timeline here, because it affects how you read older tutorials and Stack Overflow answers: New Architecture support existed as an opt-in, experimental flag for several releases before Bridgeless Mode became the default in 0.74, and before the whole package became the default in 0.76. A blog post or course video from a couple of years before this article that describes manually enabling the New Architecture, or warns that it is unstable, was accurate for its time but describes a transitional period that most current projects have already moved past.

What This Actually Means If You Are Building an App

If you are starting a new React Native project today, you do not need to do anything special to get JSI and the New Architecture — it is the default, and your day-to-day component code (JSX, hooks, styling) looks identical either way. The architecture change is almost entirely invisible from inside a typical screen’s code; it shows up in how fast gesture-heavy or animation-heavy screens feel, and in startup time, not in syntax you write.

Where it becomes visible is dependency choice: before adding a third-party native module to an older, not-yet-migrated project, it is worth checking whether that library has New Architecture support, since a library built only around the old bridge APIs will rely on the interop layer rather than getting the full performance benefit. For a brand-new project on a current React Native version, this is rarely a concern anymore — the ecosystem has largely caught up.

The one-sentence summary worth remembering for an interview: the old bridge was asynchronous and JSON-serialized because it had to cross two completely separate memory spaces every time; JSI removes that crossing cost by letting JavaScript hold direct references to native C++ objects and call them synchronously, and TurboModules plus Fabric are JSI applied specifically to native modules and to the UI rendering tree.

For an Indian developer preparing for interviews at companies that use React Native — which, given the framework’s popularity for shipping cross-platform apps quickly, is a real share of both startups and larger product companies — this is also a genuinely common system-design-adjacent interview question. Being able to explain the asynchronous-and-serialized nature of the old bridge, and name the three pieces (JSI, TurboModules, Fabric) that replace it along with what each one specifically does, demonstrates an understanding of the framework beyond “I know how to write components,” which is exactly the kind of depth that separates a strong fresher from an average one in a technical round.

Frequently Asked Questions

Do I need to rewrite my app to use the New Architecture?

If you are starting a new project on a recent React Native version, you get it by default with no extra work. If you are upgrading an older app, it is a migration, not a rewrite — your component code stays the same, and the main work is verifying your native module dependencies support the New Architecture (most popular ones now do) and testing thoroughly, since this does touch how native code is wired underneath.

Is JSI something only React Native uses?

JSI itself is described by React Native’s documentation as a general-purpose, lightweight interface for embedding a JavaScript engine into a C++ application — it is not tied to React Native’s component model. React Native is simply the project that built it and popularized it for this exact JS-meets-native problem.

What is Hermes and how does it relate to JSI?

Hermes is a JavaScript engine built specifically for React Native, optimized for fast app startup and lower memory use on mobile devices. JSI is the interface layer that lets Hermes (or another JSI-compatible engine) talk to native C++ code; Hermes is one specific engine that implements that interface, not the interface itself.

Will my old native modules stop working after the New Architecture becomes default?

Not immediately — React Native ships an interoperability layer specifically so modules written for the old architecture can keep functioning while the ecosystem migrates. They will not get the full synchronous-call performance benefit until they are updated, but they generally do not just break.

Does the New Architecture make React Native exactly as fast as fully native code?

No single architecture change makes that claim true — React Native still runs JavaScript, which has its own execution cost compared to compiled native code. What JSI and the New Architecture remove is a specific, measurable overhead (serialization and async queueing for every cross-boundary call), which closes a real performance gap for gesture- and animation-heavy screens without changing the fact that you are still running a JS runtime alongside native UI.