We’re Building a New React Native Framework

Michał Pierzchała
No items found.

In short

Our new React Native framework speeds up enterprise app development with smart native build caching on CI with ability to reuse these builds across workflows, end-to-end tests, and local developers environments thanks to integrated CLI—all while staying flexible and self-hosted. Read to learn how it works and what’s coming next.

Last week, we announced something we’ve been working on for the last couple of months: a React Native framework designed specifically for mature Enterprise apps. It’s the result of years of experience building tools and contributing to large-scale projects of our clients.

Why a new framework

Or rather—why not? As many of us, React Native developers, have a background in the web or are exposed to the web libraries, frameworks, and ideas that are cross-pollinating to the React Native Community, we’re familiar with hundreds of JavaScript frameworks coming out every day. And somewhat fewer that are specific to React.

In the realm of building cross-platform native apps—mobile, desktop, TV— there’s no such diversity. We have Expo, excellent for new and growing projects, with great DX and loads of services to build and deploy apps quickly as part of their EAS cloud, focusing on mobile and web platforms. Recently, we’ve also been using One, which brings Vite bundler and Zero sync engine to React Native, focusing on web and mobile, still in its early days and beta phase. And that’s pretty much it.

At the same time, on a daily basis at Callstack, we’re serving clients that usually have large teams, building complex apps for years, accumulating tech debt, becoming slower and slower to iterate, where time is wasted on waiting for builds and onboarding web engineers into intricacies of mobile platforms. According to the React Native Framework RFC, almost all of these companies are building their own frameworks based on the Community CLI—they just don’t open source them to make them available for everyone, and for good reasons.

As a maintainer of the Community CLI, I have quite the exposure to how this tool is used (and misused) in various projects. When we evaluated how our clients use it and what our developers are challenged with, we noticed that these companies encounter similar challenges that they address uniquely:

  • High build times with no reuse across CI jobs and dev team.
  • Months just to integrate a third-party cloud vendor.
  • A mixed tech stack that makes adding React Native feel like pulling teeth.

We also have clients who ship their products to 10+ platforms or have brownfield setups, embedding React Native in their native apps, so using anything other than the Community CLI is not an option for them.

The early experiments with UX

So, in the spirit of Open Source, we ran some experiments with a small team of Maciej Jastrzębski and Szymon Rybczak. First, we refreshed the UI of the CLI to focus on local building experience, picking devices or simulators to run an app in a smarter and more interactive manner.

We felt that was cute, but it required a massive rewrite of the Community CLI, which wasn’t really possible to backport without breaking changes. Since this package is used by hundreds of thousands of developers, we wanted to be super cautious—we wouldn’t need to be with a new tool, though, so the experiments continued.

Knowing that enterprises we work with have little to no build reuse across CI jobs and the dev team, we toyed with the idea of dragging and dropping builds to our simulators and emulators, which works pretty well both for iOS and Android.

If developers had easy access to native build artifacts, they wouldn’t really need to build their projects locally. Not to mention installing CocoaPods (and all the hurdles it takes for web developers), running Xcode build and Gradle. Crossing it all off the list would give you some nice time savings over the course of the week and even more over a month. With teams of 20-80 developers, that time adds up ridiculously fast.

Reusing builds across CI jobs

At the same time, Maciej was investigating the other part: the build reuse. We’ve seen our clients approach speeding up builds by caching various parts of the pipeline, with varying success. But usually the cache was flaky, invalidating more often than necessary, and thus unreliable. We’ve even seen teams that deliberately disabled all kinds of caching because fixing problems related to it caused them more engineering capacity than it was worth.

So, we picked the most popular CI option we surveyed across our clients and decided to build some prototypes for reliable build reuse on GitHub Actions. With GitHub Artifacts storage, we had a “backend” to store builds across various jobs. Now, we needed a more robust caching solution.

We initially started to build one, and then it struck us that months earlier, Expo released a great piece of software: @expo/fingerprint, which allows them to match JS bundles to particular native versions associated with that bundle by calculating a unique hash. This is super helpful for over-the-air updates with Expo Updates. But… It would also suit nicely for reliable build caching. Knowing the hash of our project, we can reduce the build times by completely avoiding them.

Because the best build is no build.

That allowed us to put the hashing mechanism on a GitHub action. Instead of dragging and dropping the binary to a simulator, we figured integrating it with a single command, such as run:ios or run:android, run side-by-side with a Metro or Re.Pack dev server, would be an even nicer experience for developers.

So the ultimate experience goes like this:

  1. Install npm dependencies.
  2. Run the dev server.
  3. Run the iOS or Android command, which will fetch the build from the remote cache stored on GitHub Artifacts.

And that’s it. In a few seconds and three commands, you’re ready to ship without installing native dependencies or running a local build. And it’s all hosted on an infrastructure you 100% control.

Testing it in our clients’ codebases

Now that we had a fully fledged CLI and GitHub Actions that it could connect to, it was time to integrate it with some of our clients. We found a few early adopters, one of which we estimated lose over 1000 hours (yes, you read correctly, 125 days) of engineering and testing capacity every month due to excessive build times across the organization.

Once the native artifact is cached, all subsequent builds are dramatically faster. In the case of one of our clients, from 34 minutes to 3 minutes, which is roughly the time it takes to spin up the CI machine and install npm dependencies for that project. A 10x decrease.

Faster builds, from 34 minutes to 3 minutes

But these are unsigned APP and APK builds for developers, who can download them through our CLI and run latest JavaScript local dev server with Metro or Re.Pack. Let’s say that’s the easy part.

For our apps to be usable by internal or external testers, we need to build our apps in a way that they’re runnable on devices. That’s where we need app signing, as well as re-signing—we need that fresh JavaScript bundle available for our test builds, or else an old cached one will be picked.

We’ve solved that as well, and for one of our clients, it resulted in a workflow that allowed them to run e2e tests on a device farm on every commit of every pull request—something that was not achievable on their own infrastructure before using our framework.

Testing automation that reuses builds with up-to-date JavaScript bundle
Testing automation that reuses builds with up-to-date JavaScript bundle

Our Principles

We build the new framework with a clear focus: to serve large teams and complex apps. These projects require flexibility, the ability to host everything on their own, deploy to as many platforms as possible, and decrease onboarding time. That’s why our engineering design choices focus on:

  • Modular design—add your supported platforms and plugins, and integrate existing tools; you can build around our framework
  • Self-hosting—use your own infrastructure without relying on third-party cloud vendors; whether you’re using GitHub Actions or soon Amazon S3 and BitBucket, we got you covered.
  • Incremental adoption—easily migrate from Community CLI or an existing native app.

We’re not focusing on new projects or smaller teams yet (but we have a CLI to spin up new projects if you need to). At least not yet. We’re laying the foundation for more integrated features to come: OTA, React Server Components, mobile microfrontends with Re.Pack, more platforms, bring-your-own-remote-cache-provider, just to name a few.

How you can try it

Our framework is currently in an early development phase and not yet publicly available. To get first-hand news on the project development, join our waiting list. For selected apps, we may consider giving you access to an early preview once we finalize the documentation, which we are working on right now. Let us know by answering the waiting list email or by using the contact form.

Our plan is to open-source this framework as soon as possible—hopefully in the upcoming weeks. We want to ensure some base level of polish, DX, and docs, so you can have an easy time giving it a try without a hotline to our team. Building such tools privately is certainly not within our comfort zone, so expect it sooner than later!

Latest update:
March 14, 2025

FAQ

No items found.
React Galaxy City
get in touch

This information will be used only to contact you. For details, check our Privacy Policy.

React Galaxy City
get in touch