skip to content

In a React Native 0.87 app, does JavaScript still talk to native code through the bridge, and what replaced it?

level: juniorimportance: should knowfreq 50%

answer

  1. bridgeless by default since 0.74
  2. New Architecture only since 0.82
  3. Turbo Native Modules and Fabric
  4. modules load lazily
  5. interop for old libraries

basics

~10 s

No. React Native 0.87 runs bridgeless: JavaScript calls Turbo Native Modules and drives the Fabric renderer through JSI, with no serialized message queue. Libraries still written for the bridge work through interop layers.

solid answer

~40 s

No. Bridgeless mode became the default with the New Architecture in 0.74, and since 0.82 the New Architecture is the only one, so a 0.87 app has no bridge in its runtime path. **JSI** replaced it: **Turbo Native Modules** are called directly, synchronously or with promises; the **Fabric** renderer receives React's updates in C++; modules load lazily instead of at startup; and globals such as timers are bound from C++. Libraries still written against the old module or view APIs run through interop layers, and some bridge-era files still ship while removal continues, which is why `MessageQueue` can still be found in the repository. The practical consequences are faster startup, synchronous native calls where they make sense, and better startup error reporting.

go deeper

for a junior

State that current React Native has no bridge in its runtime path and that JSI, Turbo Native Modules and Fabric took over its jobs.

for a middle

Map each bridge job to its bridgeless replacement, including lazy module loading and C++-bound globals, and name what interop still covers.

for a senior

Recognize bridge-era code in a dependency tree and explain why it is the first suspect after an upgrade, even while interop keeps it running.

for a principal

Plan for interop layers eventually going away: track which dependencies still rely on them and budget their replacement.

## Short answer: no A React Native 0.87 app runs **bridgeless**. JavaScript reaches native modules, the renderer and platform events through **JSI**, the C++ interface to the JavaScript engine, not through the legacy bridge's serialized message queue. The history in three steps: | Release | What happened | |---|---| | 0.74 | Bridgeless mode became the default whenever the New Architecture was enabled | | 0.76 | The New Architecture became the default for new and upgraded apps | | 0.82 | The New Architecture became the only architecture; `newArchEnabled=false` is ignored | From 0.84 on, legacy code started to disappear: legacy Android bridge classes such as `CallbackImpl`, `CxxModuleWrapper` and `BridgeDevSupportManager` were removed, and legacy architecture code is compiled out of iOS builds by default. ## What the bridge used to do, and what does it now The bridge was not only a pipe for method calls; it also booted the app. Bridgeless mode replaced each job: | Bridge job | Bridgeless replacement | |---|---| | Carry native module calls as batched JSON messages | **Turbo Native Modules** called through JSI, synchronously or with promises | | Carry view updates to a native UI manager | The **Fabric** renderer, which React drives through JSI in C++ | | Register and initialize every module at startup | Modules load **lazily**, on first use | | Install globals such as timers from JavaScript wrappers | Globals bound directly from C++ on the runtime's global object | | Deliver native events as queued messages | Events dispatched to the JS thread through the renderer and the runtime scheduler | Two effects are visible to users and developers: 1. **Faster startup**: there is no bridge to initialize and no eager module setup. 2. **Better failure reporting**: JavaScript errors early in startup are reported more reliably, and there are fewer crashes from undefined behavior in the boot path. ## What "bridgeless" does not mean - **It does not mean every library was rewritten.** Libraries still written against the old module or view APIs run through **interop layers** that adapt them to the New Architecture. The React Native team has said the interop layers stay for the foreseeable future. - **It does not mean legacy source files are gone.** Parts of the JavaScript bridge layer, such as `MessageQueue`, still ship in the package while removal continues, so you can still find them in the repository. - **It does not mean everything is synchronous.** Native modules still expose asynchronous methods; bridgeless removes the queue, not the choice. - **It does not mean JavaScript is multi-threaded.** Your code still runs on the single JS thread. ## What changes for everyday app code Very little, and that is deliberate. App code keeps importing components and modules the same way; the difference sits one level down: - a module's JavaScript entry point looks it up with `TurboModuleRegistry.getEnforcing<Spec>('Name')` against a typed spec, instead of reading `NativeModules.Name`; - a method declared as returning a value in the spec returns it directly, while methods declared as returning a `Promise` stay asynchronous; - the dependency list is where the architecture shows: a library that has not moved to specs is the one riding on interop. ## How to recognize bridge-era code When reading a codebase or a library, these are signs it was written for the bridge: - native modules reached through `NativeModules.X` without a typed spec; - events sent through a device event emitter with JSON-shaped payloads only; - Android code that depends on `CatalystInstance` or a React instance manager; - callbacks used where a synchronous return value would now be possible. Such code may still work through interop, but it is the first place to look when something breaks after an upgrade. ## In an interview For a junior screen, the expected answer is short: the bridge is gone from the runtime path, JSI replaced it, and the New Architecture has been mandatory since 0.82. Adding one concrete consequence, such as lazy module loading or synchronous native calls, shows that you know why the change was made, not only that it happened. Expo SDK 57 apps run React Native 0.86, so the same answer applies to them.

  • In React Native, why does removing the bridge make app startup faster?
    The bridge had to be created and every registered native module set up before JavaScript could run, and some globals needed JavaScript wrappers that called native modules. Without it, modules load lazily on first use and globals such as timers are bound directly from C++ on the runtime, so less work happens before the first screen.
  • After upgrading to React Native 0.87, an old library still uses NativeModules.X without a spec. Why can it still work?
    React Native keeps interop layers that adapt modules and views written for the legacy APIs to the New Architecture, and the team has said they stay for the foreseeable future. The library runs through that adapter rather than a bridge. It is still the first suspect when something breaks, because interop does not cover every legacy API.

saying these in an interview costs you the question

  • React Native 0.87 apps can switch back to the bridge with newArchEnabled=false.
  • Bridgeless means every native call is now synchronous.
  • Old libraries stop working entirely once the bridge is gone.
  • Bridgeless mode moves JavaScript onto several threads.
  • The bridge still carries view updates, only module calls use JSI.