No rewrite required: How to bring Expo into mature native apps

DevelopmentReact Native7 minutes read

Gabriel Donadel

Gabriel Donadel

Engineering

Brent Vatne

Brent Vatne

Engineering

Skip the costly rewrite. Use Expo and React Native to modernize your app incrementally, adding new screens and features in days, not months.

No rewrite required: How to bring Expo into mature native apps

Introducing a new technology into a mature native app can be both tempting and daunting. But it doesn’t have to be. Because you don't need to do a rewrite. Expo is additive.

You can adopt Expo incrementally, shipping one new feature (or even one view) at a time, neatly integrated into your existing app.

We want it to be easy to use React Native for as much or as little of your app as you need. We think there are many compelling reasons why you might want to take an afternoon to try it out in your app, so we'll list those and then give a quick outline of how to start incrementally integrating Expo.

Common reasons teams choose incremental adoption of Expo

Uncompromised speed and flexibility. Those are the powers that React Native brings to dev teams. Below we've listed some of the common reasons for incremental adoption of React Native and Expo that we observe across our user base. Fundamentally, they all map to speed and flexibility.

It’s important to understand that adoption is possible without compromising existing native investments or rewriting your app. For a lot of teams, this makes adoption of Expo a fairly simple decision.

Adopting React Native for platform parity

Sometimes a product team needs to ship a new feature across iOS and Android at the same time, but native teams have different priorities or limited bandwidth. By building that feature once in React Native, they can deliver it in both apps simultaneously. For example, a team might implement a new media player UI or subscription flow this way, ensuring both platforms stay in sync.

Shared components

Larger organizations often have multiple apps with overlapping functionality. With React Native, you can introduce shared JavaScript components that reduce duplication and improve consistency across apps. A design system or custom charting library are common starting points, allowing you to write it once, and every app benefits.

Teams split by domain

Not every engineer needs to be an expert in native development. Mobile platform engineers can maintain the underlying iOS and Android infrastructure, while product teams focus on building features in React Native. This division of responsibility makes it easier to onboard new engineers, scale teams, and move faster.

Add a new feature quickly (one screen)

One of the lowest-friction entry points is building a new feature entirely in React Native, maybe onboarding, settings, or a chat view. The rest of the app remains unchanged, but you get the benefits of React Native’s fast iteration cycle.

Migration strategy

Aging native codebases can be difficult to maintain. Rather than rewriting the whole app, teams can gradually replace parts of the app with React Native. Over time, more of the app benefits from faster development cycles, while still leveraging the stability of the existing native foundation.

With so many clear entry points, the real question isn’t why to adopt Expo incrementally, but where your team should begin.

Where to start an incremental adoption of Expo

Integrating Expo into your project can be done in a few quick steps. The following is a short summary. (For all of the code and details refer to “How to add Expo to an existing native app”).

  • Create an Expo project inside your existing app project npx create-expo-app my-project
  • Set up your project structure A standard React Native project places native code in android and ios directories. The specifics of how to do this depend on your project, but it could be as simple as creating the directories and moving your projects there or configuring a custom project root. For example: mv /path/to/your/ios-project my-project/ios/
  • Configuring your native project Now that you have your project set up, the next step is to connect React Native with your existing native code. This involves making a few small changes on both Android and iOS so your app knows how to load and run React Native.
    • On Android, you’ll update a few key files:
      • Gradle files: Update settings.gradle, build.gradle, app/build.gradle, and gradle.properties to include the React Native Gradle Plugin (RNGP) and required properties.
      • AndroidManifest.xml Add any necessary permissions (learn more).
      • MainActivity: Initialize and load your React Native application so it can render views inside your existing activity.
      • ReactActivity: Create and present a ReactActivity to render your components.
    • On iOS, the changes are similar in spirit:
      • Podfile: Add React Native dependencies and run pod install.
      • Xcode project: Add a new build phase that bundles your JavaScript code.
      • Info.plist: Configure app settings required by React Native.
      • ReactViewController: Create and present a ReactViewController to start the React Native runtime and render your components.

Once these changes are in place, your app is ready to run React Native code alongside your existing native screens. Now build your native app and run the following command in the React Native directory to start the Metro bundler: npx expo start

Example integrations in open-source apps

We’ve put together a repository that integrates Expo into several open source apps that we found on GitHub, to provide concrete examples in the context of real-world apps. The repository includes Android and iOS (both UIKit and SwiftUI) examples and is available at https://github.com/expo/expo-brownfield-examples.

For each example integration (AntennaPod, IceCubesApp, NetNewsWire, and simplenote), refer to the commit history in the submodule to see how Expo was added to the app.

The repository also includes examples of integrating Expo into blank iOS and Android apps. You can use these links to reference the BlankIOS and BlankAndroid submodules.

Current limitations and what’s next

Most of Expo's libraries will work without further changes, but some may require an additional setup to listen for the app lifecycle. You can check the full details of how to configure lifecycle listeners in your app for Android here and iOS here.

Libraries like expo-dev-client are not supported yet, but we're working on adding support for that in our next SDK release. You can check the list of unsupported libraries at https://docs.expo.dev/brownfield/overview/.

We're committed to continuing to improve this integration and have been testing it with multiple open-source apps. You can check some examples in https://github.com/gabrieldonadel/expo-brownfield-experiments.

brownfield apps
native apps
incremental migration
incremental adoption

Create amazing apps, in record time with EAS

Learn more