Migrating a React Native Library to the New Architecture

Authors
Oskar Kwaśniewski
Software Developer
@
Callstack

This article is a bird's-eye view of migrating a React Native library to the New Architecture. At first, I wanted to go into the details. However, due to the pace of changes and ever-evolving new patterns, I decided to walk you through the migration steps with the helpful resources I found.

I had the pleasure of working on migrating react-native-slider and also the Android part of react-native-pager-view.

The New Architecture is here

If you are following things that happen in the React Native world, I’m sure you heard about the New Architecture, including Fabric and Turbo Modules.

If you are new to the “New Architecture” world, check out Lorenzo's at React Native Wrocław Meetup this year.

There is also a podcast episode on the New Architecture in which Łukasz Chludziński talks with Nicola Corti, a member of the React Native core team.

What’s Fabric?

Fabric is React Native's new concurrent rendering system, a conceptual evolution of the legacy render system. It allows for all the features React 18+ brings to the table. Fabric is one of the 4 pillars of the New Architecture that React Native core team is working on since 2018.

Thanks to Fabric, instead of using the bridge which communicated with serialised JSON messages, we can communicate using JSI (second pillar), which is a C++ API for interacting with any JS engine. Ultimately JSI is going to replace the bridge (it’s the “bridgeless” project) but for now they live side by side, which is good because it can speed up your migration when you stumble upon some edge case which is not supported yet.

The third pillar is Turbo Modules. It’s an interface on top of JSI, that leverages CodeGen (the last pillar) to generate C++ code from our TypeScript or Flow spec.

If you want to try out the new Architecture in your app, make sure to address a few prerequisites first.

Steps to migrate a library

  1. Writing TypeScript spec for Codegen
  2. Migrating deprecated JavaScript APIs
  3. Extending react-native-codegen generated interfaces on native platforms (Android / iOS)
  4. Done ✅

This may seem like a lot of work but with the documentation getting better and more developers getting involved in the process, it becomes easier every day. It is also worth noting that the first two steps can be done even before migrating to the New Architecture - when preparing your library for migration.

Using an official migration guide for library creators

There is an official migration guide that you can follow: React Native New Architecture Libraries. It has examples creating simple Fabric component and also a Turbo Module. There is also another guide for migrating your whole app for the New Architecture.

These examples really help to get you going with the migration. However, most of the time you will end up browsing the source code of other libraries (at least I did). So another helpful resource is a list of already migrated libraries.

Creating a library from scratch

If you want to create a new library or just scaffold a New Architecture setup for your existing library, you can use react-native-builder-bob which recently received a lot of new features!

Here is a screenshot of scaffolding a new library with create-react-native-library. As you can see, there are new options for Turbo Modules.

scaffolding a new library

We have used it to scaffold the New Architecture inside react-native-pager-view which was way faster than creating all the necessary setup by hand. Fabric support will be added soon, but it may be already there as you are reading the article. So go ahead and give Bob a try!

1. Writing TypeScript spec

Now let’s start with the migration part. The goal of this step is to write a specification of your module which is a set of types written in Flow or TypeScript that defines all the APIs provided by the native module. Using a spec allows CodeGen (code generation tool for React Native) to generate native code for each platform during build time. To find more about it, refer to the docs.

Here is how the generated files look like for Android.

Generated files on Android

It sounds so good in theory. In reality, however, not everything is working as expected (yet!). Let’s go over a few issues I’ve stumbled upon.

Unsupported props between platforms

As for now CodeGen doesn’t support platform specific specs, so keep in mind that you need to define types for every platform in your spec file. If you have any props that’s not supported on iOS it’s not a problem because you can skip it inside updateProps method:

- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps
{
const auto &oldScreenProps = *std::static_pointer_cast<const RNCSliderProps>(_props);
const auto &newScreenProps = *std::static_pointer_cast<const RNCSliderProps>(props);
if (oldScreenProps.step != newScreenProps.step) {
slider.step = newScreenProps.step;
}
// Handle only supported props here
}

snippet source code

However, Android handles props a little bit differently. It generates setters for all of the props which need to be overwritten.

The best way to tackle this problem is to override those setters with empty functions like here:

// iOS only prop
@Override
public void setVertical(ReactSlider view, boolean value) {}

Union types

Typescript union types are not yet supported by Codegen, but we can get around this limitation by using a Codegen helper type named WithDefault.

Here is what this “hack” looks like in react-native-pager-view:

overScrollMode?: WithDefault<'auto' | 'always' | 'never', 'auto'>;

If you ever get stuck, you can go through these resources:

It’s also worth mentioning that Meta is working on making Codegen support more advanced.

2. Migrating deprecated APIs

Both projects I’ve worked on haven’t used any of the deprecated APIs. However, one of the most used ones is setNativeProps. It fundamentally breaks the model of the New Architecture leading to skipping all the rendering steps and desynchronising UI between React Native and what’s shown to the user. You can read more about it on GitHub.

If you happen to use one of the APIs from this list though, it should be pretty straightforward to just replace them. You can find helpful resources in the Working Group discussion on the New Architecture.

3. Implementing Codegen generated interfaces on native platforms (Android / iOS)

This step is probably the most time consuming and it depends on the complexity of your library.

Android part is easier since we are having two source sets newarch / oldarch which get loaded accordingly to a newArchEnabled flag in build.gradle and then one SharedImpl file which you can share between implementations.

However, iOS is based on creating a new .mm files which are implementing Codegen generated protocols. Depending on what’s your library doing you may need to create separate implementations for new / old arch or share code between them.

Using Swift

Both of the libraries I’ve worked on haven’t used Swift. But, unfortunately, it’s not officially supported at the moment. The official Meta team statement is that they are “looking into it.” Codegen generates native ObjC++ files (.mm) which are tricky to reference from Swift files.

There is a great twitter thread from Mateusz Mędrek on his experiences.

That #reactnative #Fabric & #TurboModules migration was a big pain, especially that I had Kotlin and Swift code to migrate, so it’s a big relief to have it done. And there is lots of completely undocumented stuff I wish I knew that would made it a lot easier to migrate pic.twitter.com/CSChN7GJcT

— Mateusz Mędrek (@mateusz1913) September 25, 2022

Using Kotlin

When we were migrating react-native-pager-view I had lots of build issues which resulted from using Kotlin. Due to mixing Java with Kotlin, Codegen created files which were not included. So we have added a temporary workaround which may be useful in case you are also using Kotlin in your library. Check it out here:

sourceSets {
main {
if (isNewArchitectureEnabled()) {
java.srcDirs += [
"src/fabric/java",
"${project.buildDir}/generated/source/codegen/java"
]
} else {
java.srcDirs += ["src/paper/java"]
}
}
}

snippet source code

Custom component state with C++

In some use cases Codegen isn’t enough - it’s a good starting point but It wasn’t designed to cover all the edge cases. So if your native component would like to update its frames in a synchronous way, you will need to add custom C++ state.

I haven’t added custom shadow nodes to react-native-slider in C++ yet (it’s scary but it’s on my to do list 😄). We have lots of great developers working on improving the New Architecture here at Callstack. My colleague Piotr Trocki together with Nicola Corti created a sample repository which will help you set this up. Right know the guide only covers Fabric, yet there is already an open issue to support Turbo Modules.

If you are afraid of C++ you can make use of the good old bridge as it’s still being initialized. But keep in mind that one day there will be bridgeless mode added.

Build times

While I was working on migration of react-native-slider we were using react-native 0.69 which had a major flaw in terms of build times. When it was time to create a fresh build without any cache, I could go and make myself a coffee ☕️ and then go for a walk. It took around 15-20 minutes. The good thing is that the community has added an --active-arch-only flag which will decrease your build times even 3x. And the core team is actively working on bringing the build times down by leveraging prebuilt artifacts outside of NPM.

In the latest nightly version of React Native there is a huge boost in build speed using new arch on Android 🚀🏎️

Combined with the `--active-arch-only` flag we went from around 9 minutes to ONLY 3 minutes 👀🔥

Try it out yourself with: `yarn android --active-arch-only` pic.twitter.com/ZhgpAHSbeC

— Oskar Kwaśniewski (@o_kwasniewski) October 26, 2022

Also keep in mind that you will need a pretty good machine to play around with new architecture. I saw few comments to my tweet indicating that building app (using new architecture) took them around 30 minutes. Of course once the build is cached it will be faster.

4. Now it’s time to release a migrated library

Every migrated library brings us closer and closer to finally enable the New Architecture in the production apps. Meta is using Fabric in their Facebook app since last year, but most of us are probably still blocked by the libraries that don’t have support for the New Architecture yet. If you want to track the status of migration for libraries, there is a dedicated discussion page. And if you are working on migrating your library, also add it there so it will be listed in the React Native Directory as a library that supports the New Architecture.

React Native Directory

Try React Native Slider with Fabric

  1. Create a new app using React Native CLI: npx react-native init NewArchApp
  2. Add a React Native slider yarn add react-native-slider
  3. [iOS] Run RCT_NEW_ARCH_ENABLED=1 pod install inside ios folder
  4. [Android] Set newArchEnabled=true in gradle.properties

In the New Architecture mode, components that are not yet compatible will show a red box: Unimplemented component: <ComponentName>, and you will likely notice them. In that case, please let the library maintainers know about it.

Summary

Even though the React Native New Architecture is out there for a good while now, it’s still a challenge to do migrations, especially Fabric. The React Native core team put a good amount of work into making this effort easier. There’s a lot new documentation and up-to-date repositories on how to migrate both libraries and apps. And it’s still not enough. We keep finding the gaps in the official docs and often need to dive into the source code and commit history to figure out the rationale behind some API decisions.

Nicola Corti from the core team frequently mentions at conferences and podcasts that now it is the best time for apps to migrate. That’s because now they have a dedicated capacity to help the community solve their app and library problems in a close cooperation. They can’t promise whether they’ll have this capacity in a year from now.

At Callstack, together with my fellow developers, we often took this opportunity to reach out directly to the core team members, be it around the New Architecture’s autolinking, or migrating Slider, PagerView and Bob. And they are always utterly helpful, even though not available on-demand.

Although this may not be the best time for your app to adopt the New Architecture, I strongly encourage you to try. Turn on the build flag, check which components and modules are missing support. Post your problems and findings in a GitHub discussion group. The New Architecture is the biggest thing that has happened to React Native since its inception.

Insights

Learn more about React Native

cover

Oct 7Talk

Building a Modern Navigation Library: The Hard Parts

At reactCon, Satyajit Sahoo explores the design choices behind React Navigation 8 and the challenges of creating a navigation library that works across platforms.

cover

Aug 4Podcast

VisionCamera V5 With Marc Rousavy

Marc Rousavy joins Mike Grabowski to unpack VisionCamera v5, Nitro Modules, the constraints API, migration work, and what changed for React Native camera apps.

cover

Jul 1Podcast

Redraw for Effects Beyond Skia

William Candillon talks about where Redraw fits next to Skia, from physics-based rendering to camera effects, AR, and new UI patterns.

cover

Jun 29Article

Less Boilerplate, More Type Safety: React Navigation 8's Typed Hooks

React Navigation 8 improves typed hooks with screen-aware types, less boilerplate, and fewer unsafe casts in shared UI and nested navigators.

cover

Apr 30Live Stream

Build a React Native App With the TanStack Stack

Join Callstack’s live stream on building a React Native app from the ground up using the TanStack toolbelt.

cover

Apr 28Article

React Native production lessons from React Universe Meetup x Zalando

Lessons from React Universe Meetup x Zalando on React Native in production, covering brownfield performance, feed video, native API access, and on-device AI.

cover

Apr 23Talk

Shipping iOS Live Activities with React: The Voltra Story

How Voltra enables Live Activities and Dynamic Island views using React without writing Swift. A deep dive into iOS constraints and architecture.

cover

Mar 5Article

Keeping Screens Visible With <Activity mode="hidden">

A cross‑platform workaround to keep screens visible while <Activity mode="hidden"> unmounts effects on inactive screens by setting mode="hidden".

cover

Feb 25Article

React Native Monorepo With pnpm Workspaces

Set up a React Native monorepo with pnpm workspaces for mobile and Next.js web, including Metro fixes, hoisted node-linker, and shared packages.

cover

Feb 18Article

A Practical Guide to React Native Monorepo With Yarn Workspaces

Learn how to set up a React Native monorepo with Yarn workspaces for mobile and web, including Metro, Gradle, and shared packages.

cover

Feb 17Podcast

From Teddy Bears to Voice Agents: Kraen Hansen on Voice AI, Local-First & App Security

What happens when you combine voice agents and teddy bears? A hands-on look at local-first systems, voice AI, and security lessons from poking real apps.

cover

Jan 28Article

Bringing CSS Clipping to React Native

A deep look at how a single CSS feature moves through React Native’s New Architecture, from JavaScript styles to native rendering on iOS and Android.

cover

Mar 19Tutorial

The Hard Way vs. The React Native AI SDK Way: Stop Writing Custom Modules for Every Model

Learn how React Native AI uses a unified provider model to switch between cloud and on-device LLMs without rewriting application logic, enabling reliable AI experiences online and offline.

cover

Mar 12Tutorial

Build Smarter Apps: Tool Calling & AI Orchestration Explained

Learn how on-device models in React Native can call tools, interact with external APIs, and return structured outputs you can use directly in application logic.

cover

Mar 5Tutorial

MLC LLM + React Native: On-Device AI Without the Pain

Learn how to run third-party on-device LLMs in React Native using MLC LLM. Choose your models, run them efficiently across platforms, and keep a unified JavaScript API with React Native AI.

cover

Feb 24Tutorial

Intelligent Fallbacks with Apple Intelligence in React Native

Learn how to use Apple’s built-in Foundation Models in React Native apps, reduce memory usage, and enable fast, fully on-device AI with React Native AI Apple.

cover

Feb 10Tutorial

What Is the React Native AI SDK? A Complete Intro & Quickstart

Learn why on-device AI matters for React Native apps, how local LLMs behave offline, and what problems React Native AI is designed to solve from day one.

cover

Jan 29Tutorial

Implementing an Android TurboModule with Kotlin

Learn how to implement a React Native TurboModule on Android, from the TypeScript spec and Codegen to the Kotlin implementation and package registration.

cover

Jan 19Tutorial

Working with Different Threads in Swift TurboModules

Learn how JavaScript, native module, background, and main threads interact in React Native TurboModules, and how to use GCD queues to run async work safely in Swift.

cover

Jan 9Tutorial

How to Add Type-Safe Constants to Swift TurboModules

Learn how to expose typed, immutable constants from a Swift TurboModule using Codegen, and get full end-to-end type safety between native and JavaScript.

cover

Dec 30Tutorial

Adding Event Emitters to Your TurboModule in Swift

Learn how to emit native events from a Swift TurboModule and subscribe to them in JavaScript, including proper memory management and Codegen integration.

Writing a Swift TurboModule in React Native

Dec 22Tutorial

Writing Your First TurboModule in Swift

Learn how to build a fully working TurboModule in Swift, integrate it with Codegen, and bridge it through Objective‑C inside an Expo app.