Agency

Migrating to the New Architecture: Fabric and JSI in React Native

Fabric modernizes rendering, while JSI (JavaScript Interface) enables direct and more efficient communication between JavaScript and native or C++ code.

LAST UPDATED: June 11, 2025
9 min read
Migrating to the New Architecture: Fabric and JSI in React Native

React Native's New Architecture represents a fundamental shift in how JavaScript communicates with native code. Fabric modernizes rendering, while JSI (JavaScript Interface) enables direct and more efficient communication between JavaScript and native or C++ code. Together with TurboModules and the new rendering pipeline, these technologies address important limitations of React Native's legacy bridge. But migration is not simply a matter of switching a configuration flag. Native modules, third-party dependencies, rendering assumptions, threading behavior, and application performance can all change. A successful migration requires understanding what is changing, identifying compatibility risks early, and moving toward the new architecture incrementally rather than treating it as a one-day upgrade.

Why React Native Needed a New Architecture

The original React Native architecture relied heavily on a centralized bridge between JavaScript and native code.

A simplified representation looks like:

JavaScript
    │
    ▼
   Bridge
    │
    ▼
Native Code
    │
    ▼
Platform APIs

The bridge made it possible to build native experiences using JavaScript, but communication across the boundary could become expensive for applications with heavy interaction between JavaScript and native code.

The limitations became more noticeable with applications involving:

Frequent native calls

Complex animations

Large data transfers

Real-time interactions

Native-heavy libraries

High-frequency UI updates

The New Architecture changes this model.

Instead of relying on the same centralized communication mechanism, React Native moves toward:

JavaScript
    │
    ▼
   JSI
    │
    ├── Fabric
    ├── TurboModules
    └── C++ / Native

This creates a more direct and flexible foundation for communication between JavaScript and native systems.

Legacy Architecture vs. New Architecture

The difference can be simplified like this.

Legacy

JavaScript
    ↓
Bridge
    ↓
Serialized Messages
    ↓
Native

New Architecture

JavaScript
    ↓
JSI
    ↓
Native / C++

The goal is not simply to remove one layer.

The New Architecture also changes how React Native handles:

Rendering

Native modules

Threading

Component updates

Data communication

This makes the architecture more capable—but also means migration requires more than changing one setting.

What Is Fabric?

Fabric is React Native's modern rendering architecture.

Its purpose is to make the rendering system more flexible and better aligned with modern React.

A simplified flow is:

React
  ↓
Component Tree
  ↓
Fabric Renderer
  ↓
Native View
  ↓
iOS / Android UI

Fabric is designed around a more modern rendering pipeline and supports tighter integration between React's rendering model and native UI.

This matters for applications with:

Complex interfaces

Frequent updates

Animations

Large component trees

Concurrent rendering patterns

The important conceptual shift is:

Fabric is not simply a faster version of the old renderer. It is a redesigned rendering foundation.

That means native components and libraries that make assumptions about the old rendering system may need updates.

What Is JSI?

JSI stands for JavaScript Interface.

It provides a mechanism for JavaScript to interact more directly with native and C++ code.

Instead of:

JavaScript
    ↓
Serialization
    ↓
Bridge
    ↓
Native

JSI enables a model closer to:

JavaScript
    ↓
JSI
    ↓
C++ / Native

This can reduce communication overhead for certain workloads.

The biggest benefit is not simply "fewer milliseconds."

It is the architectural flexibility that comes from allowing JavaScript and native code to communicate without depending on the same serialized bridge model.

This becomes particularly useful for:

High-performance native libraries

Animations

Database engines

Cryptography

Image processing

Storage systems

Native SDK integrations

Where TurboModules Fit

The New Architecture also introduces TurboModules as the modern approach to native modules.

Conceptually:

React Native
     │
     ▼
TurboModule
     │
     ▼
Native Implementation

Compared with traditional Native Modules, TurboModules are designed to work more closely with the New Architecture and JSI.

They can support:

Lazy loading

Typed interfaces

More direct native communication

Better integration with the modern runtime

A broader architecture looks like:

             JavaScript
                  │
                 JSI
          ┌───────┼────────┐
          ▼       ▼        ▼
       Fabric  TurboModules  C++
          │       │        │
          └───────┼────────┘
                  ▼
             Native APIs

Fabric handles rendering.

TurboModules handle native modules.

JSI provides the underlying interface for direct communication.

Together, these pieces form the foundation of the New Architecture.

Why the Migration Matters

For a simple application, the difference may not immediately be visible.

For a complex React Native product, the benefits can become much more meaningful.

Potential advantages include:

Better rendering architecture

Improved native interoperability

More efficient JavaScript-to-native communication

Modern React integration

Better foundations for high-performance libraries

More scalable native module architecture

The bigger strategic advantage is ecosystem alignment.

As React Native's ecosystem increasingly targets the New Architecture, staying indefinitely on legacy infrastructure can become more difficult.

The migration question therefore becomes:

Do we want to move when the application is ready, or wait until a critical dependency forces the move?

Auditing an Existing React Native Application

Before changing architecture, inventory the application.

Start with:

React Native Version
        ↓
Native Dependencies
        ↓
Custom Native Modules
        ↓
Third-Party Libraries
        ↓
Native UI Components
        ↓
Build Configuration

Look for dependencies involving:

Android native code

iOS native code

C++

Custom ViewManagers

Native Modules

Old bridge APIs

Native event systems

Also identify libraries responsible for critical functionality:

Navigation

Animations

Storage

Networking

Maps

Payments

Camera

Push notifications

Analytics

The goal is to answer:

What parts of the application actually cross the JavaScript-native boundary?

That is where migration risk is concentrated.

Preparing Native Dependencies

Third-party libraries are often the biggest migration variable.

Your application might depend on:

React Native App
     │
 ┌───┼───────────────┐
 ▼   ▼               ▼
Maps Payments      Storage
 │     │              │
 └─────┼──────────────┘
       ▼
 Native Code

If one of these dependencies does not support the New Architecture properly, migration can become difficult.

Create a compatibility inventory:

| Dependency | Native Code | New Architecture Support | Risk | | ---------- | ----------: | -----------------------: | -----: | | Navigation | Yes | Check current version | Medium | | Storage | Yes | Check current version | Medium | | Payments | Yes | Vendor-dependent | High | | Analytics | Yes | Vendor-dependent | Medium | | UI Library | Yes | Check compatibility | Medium |

The exact status should always be verified against the dependency's current documentation and release version.

Do not assume that a popular library is automatically compatible.

Migrating Custom Native Modules

Custom native modules deserve special attention.

A legacy module might look conceptually like:

JavaScript
    ↓
Native Module
    ↓
Bridge
    ↓
iOS / Android

The modern architecture becomes closer to:

JavaScript
    ↓
JSI / TurboModule
    ↓
Native Implementation

Migration often involves:

Defining a typed interface

Generating native bindings

Updating native implementations

Updating registration

Validating lifecycle behavior

Testing asynchronous operations

The important lesson is:

Do not treat native modules as implementation details during migration. They are architecture boundaries.

Understanding the Rendering Changes

Fabric can expose assumptions hidden inside older native components.

A custom component might previously assume:

React Update
    ↓
Legacy Renderer
    ↓
Native View

Under Fabric:

React Update
    ↓
Fabric
    ↓
Native Component

This can affect:

View lifecycle

Property updates

Event handling

Measurement

Layout

Native component registration

Custom native UI should therefore be tested thoroughly.

Pay particular attention to components that:

Measure themselves

Manipulate native views directly

Depend on layout timing

Integrate with gesture systems

Perform high-frequency updates

Performance and Threading Considerations

The New Architecture changes more than communication.

Modern React Native applications may involve:

JavaScript
    │
    ├── UI
    ├── Native
    └── Background Work
          │
          ▼
        Native

Developers need to understand where work is happening.

A common mistake is assuming:

"JSI means everything is automatically faster."

It does not.

If you perform expensive work on the wrong thread, you can still create:

Frame drops

Input latency

UI freezes

High CPU usage

Memory pressure

The goal is to place work appropriately:

UI-Critical Work
      ↓
UI-Safe Execution

Heavy Work
      ↓
Background / Native Processing

Architecture still matters more than a technology label.

Testing the Migration

Do not validate the New Architecture only by checking whether the application launches.

Test:

Rendering

Screen transitions

Layouts

Animations

Lists

Gestures

Native Features

Camera

Maps

Payments

Notifications

Storage

Performance

Startup time

Frame rate

Memory

CPU

Interaction latency

Reliability

Background/foreground transitions

Network failures

Deep links

Push notifications

Device-specific behavior

A useful comparison is:

Legacy Architecture
       ↓
Baseline Metrics
       ↓
New Architecture
       ↓
Compare

Do not assume migration is successful simply because the app builds.

Testing Real Devices Matters

Simulator testing is useful, but it is not enough.

Real devices reveal:

Memory pressure

CPU limitations

GPU behavior

OS-specific differences

Thermal throttling

Background execution behavior

A performance-sensitive migration should test representative devices:

High-End Device
Mid-Range Device
Older Supported Device

The user experience is ultimately what matters.

Common Migration Mistakes

Treating Migration as a Configuration Change

Enabling the New Architecture is only the beginning.

Ignoring Third-Party Native Dependencies

One incompatible dependency can block the entire migration.

Migrating Everything at Once

Large migrations are easier to debug when broken into manageable stages.

Assuming JSI Automatically Improves Performance

JSI enables different communication patterns. Your implementation still determines performance.

Testing Only on Simulators

Native rendering and performance issues often appear differently on real devices.

Ignoring Custom Native Components

Legacy assumptions can hide inside ViewManagers and native UI code.

Measuring Only Startup Time

Look at:

Frame rate

Interaction latency

Memory

CPU

Native call performance

Crash rates

Forgetting Rollback

Always maintain a clear path back to the known-good architecture during migration.

A Modern React Native Architecture

A New Architecture application can be visualized as:

                       React Application
                              │
                     ┌────────┴────────┐
                     ▼                 ▼
               Server / JS Logic   UI Components
                     │                 │
                     └────────┬────────┘
                              ▼
                             JSI
                   ┌──────────┼──────────┐
                   ▼          ▼          ▼
                Fabric    TurboModules   C++
                   │          │          │
                   └──────────┼──────────┘
                              ▼
                       Native Platform
                      /              \
                   iOS              Android

The architecture creates clearer boundaries between:

React

Rendering

Native modules

C++

Platform APIs

That separation becomes particularly valuable as applications grow.

A Practical Migration Strategy

The safest approach is incremental.

Step 1 — Establish a Baseline

Measure:

Startup

Memory

CPU

Frame performance

Crash rate

Build time

Step 2 — Audit Dependencies

Identify native libraries and custom modules.

Step 3 — Upgrade Dependencies

Move toward versions that support the New Architecture.

Step 4 — Test Custom Native Code

Identify bridge-specific assumptions.

Step 5 — Enable the New Architecture in Development

Do not begin with production.

Step 6 — Run Automated Tests

Test both JavaScript and native behavior.

Step 7 — Test Real Devices

Include representative hardware.

Step 8 — Use a Controlled Release

Release to a limited percentage of users where your deployment process supports it.

Step 9 — Monitor

Watch:

Crashes

ANRs

Performance

Memory

Native errors

User reports

Step 10 — Expand Gradually

Only increase rollout after the new architecture demonstrates stable behavior.

When the New Architecture Is Worth It

The migration becomes especially compelling when your application has:

Complex native integrations

Performance-sensitive UI

Heavy native communication

Large component trees

Custom native modules

Long-term React Native maintenance requirements

It can also be strategically important when your dependencies and framework ecosystem increasingly assume the New Architecture.

For a simple application with few native integrations, the immediate performance improvement may be modest.

In that case, migration should still be evaluated based on:

Maintenance

Dependency compatibility

Future upgrades

Long-term platform strategy

Making the Call

Engineering leaders planning the migration should ask:

How much native code does our application actually depend on?

Which dependencies are currently blocking the New Architecture?

Do we have custom Native Modules or ViewManagers?

What performance problems are we trying to solve?

What does the current architecture cost us in maintenance?

Can we measure the application's behavior before and after migration?

Do we have a controlled rollout and rollback strategy?

Most importantly:

Are we migrating because the new architecture solves a real technical problem—or simply because it is the latest architecture?

The answer should determine the migration timeline.

Final Takeaway

Migrating to React Native's New Architecture is a shift in how the application communicates, renders, and interacts with native code.

The old model:

JavaScript
    ↓
Bridge
    ↓
Native

moves toward:

JavaScript
    ↓
JSI
    ├── Fabric
    ├── TurboModules
    └── Native / C++

The benefits can be significant:

More modern rendering

More direct native interoperability

Better foundations for high-performance applications

A more scalable native module model

But the migration has real engineering complexity.

Audit dependencies first.

Identify custom native code.

Understand rendering assumptions.

Measure performance before changing architecture.

Test real devices.

Migrate incrementally.

Monitor production carefully.

And maintain a rollback strategy.

The New Architecture should not be treated as a checkbox. It is an architectural migration—and successful migrations are measured by reliability, maintainability, and user experience, not simply by whether a build succeeds.

Fabric provides the modern rendering foundation.

JSI enables more direct JavaScript-to-native interaction.

TurboModules modernize native module integration.

Together, they give React Native a stronger foundation for increasingly sophisticated mobile applications.

The smartest migration is therefore not the fastest migration. It is the one that gives your team a clear understanding of every native boundary, proves the new architecture against real application workloads, and gradually moves the product forward without turning a framework upgrade into a production-risk event.

Frequently Asked Questions

JSI (JavaScript Interface) is a foundational piece of the New Architecture that enables direct and efficient communication between JavaScript and native or C++ code, bypassing the serialization overhead of the legacy bridge.
No, Fabric is a completely redesigned modern rendering architecture. While it is built for better performance and alignment with modern React (like concurrent rendering), native components that assume old rendering behaviors may require updates to be compatible.
Migrating involves updating custom native modules and waiting for third-party libraries (such as maps, payments, and storage) to support the New Architecture. A successful migration requires auditing all native dependencies first to avoid production-breaking incompatibilities.

Need a product built?

We build custom software, mobile apps, and web platforms for startups and enterprises.

Alejandro D.
Vatsalya R.Backend Developer
Gustavo A.
Ganeshan S.Sr. Software Engineer
Fiorella G.
Uptal JoshiSr. Data Scientist

Their team became an extension of ours — within months they'd rebuilt our entire product experience from the ground up.

BitForge
Sr. ArchitectBitForge
Read Case Study