Give your React Native app superpowers with Expo
Maybe you’ve heard of Expo, the ecosystem that brings a number of advantages to the React Native world?
For local development, Expo provides a managed workflow that removes the need to manage native projects in XCode or Android Studio. For continuous deployment, Expo’s Application Services allows for building apps in the cloud and automatically submitting them to the app stores. And for your deployed apps, Expo comes with an easy way to deploy updates to your Javascript bundle over-the-air with expo-updates
and also send push notifications with their hosted service.
But what if you already have an existing React Native app?
How do you take advantage of the world of Expo without starting from scratch?
That’s where this guide comes in: We’ll give you a heads up for how to get started with moving your existing RN app into the Expo ecosystem.
A note on Expo’s managed workflow
Before we begin, it’s important to understand the kinds of workflows that Expo provides:
Managed workflow:
Expo’s managed workflow provides the simplest pattern for native app development. It manages the XCode and Android Studio projects for you and removes the need to update and manage native code.
Bare workflow:
Expo’s bare workflow leaves all of the native configuration and code to you. You have full control over the customization of your app in return for managing the native XCode and Android Studio projects yourself.
If it’s possible, using the managed workflow provides the most benefits of working with Expo. If your app is using Expo-included features or libraries that support Expo, this is the way to go. If you do have a need for adding native code, Expo provides the option of a “config plugin.” These config plugins extend the managed workflow to generate the necessary native code for you automatically. Many libraries either provide config plugins themselves or have third party config plugins available.
If you’re not able to find a config plugin for your purpose, you have the option to either write a config plugin yourself, or switch to the bare workflow at that time. You can begin with the managed workflow and then pick an option for writing native code if and when your application requires it.
Assess your current project before migrating
As you look into diving into the Expo world, it’s helpful to gather an understanding of where your current React Native project stands. Here are a few important things to consider before diving in.
Native libraries
First, and most important, which NPM libraries does your app depend on that use native code? If the library can be auto-linked in a regular React Native project, you may not have any additional work for Expo. If the library requires manual setup, you’ll need to see if the library is directly Expo-compatible or if there is a third-party config plugin available. If not, you may need to consider moving to a different library, writing a config plugin yourself, or switching to the bare workflow.
For example
Intercom provides a React Native plugin, but it requires manual setup in your project’s native code (e.g additions in your AppDelegate.m
on iOS and MainApplication.java
on Android). To make this work in an Expo app, you’d need to add a third-party config plugin library (config-plugin-react-native-intercom
) to work with the Expo managed workflow.
Essential device features
Second, which device capabilities does your app make use of, such as the camera, push notifications, or in-app purchases? You’ll need to switch to Expo-compatible libraries for these if you hope to use the managed workflow. Generally, Expo provides first-party official libraries in this case.
Size and complexity of codebase
Third, how large and complex is your app’s source code and architecture? A more complex architecture can make large changes more difficult and a larger codebase will take more time to test and validate after migrating.
Necessary upgrade work
Last, how up-to-date is your React Native app? Each version of the Expo SDK is tied to a specific version of React Native, and if your app is not within the past couple RN versions you may need to upgrade React Native and other libraries as well.
Additionally, if you intend to upgrade other core libraries at the same time, such as a navigation or data fetching library, you’ll want to budget more time for your migration.
How to migrate your project
Now that you have an idea of what’s ahead, let’s walk through how to move your app to Expo!
1. Create a new Expo app
Since much of the plumbing for your app will be changing, most likely the simplest way to migrate to Expo is to create an entirely new Expo app, install the necessary packages, and copy your existing codebase into the new Expo project:
- Initialize your Expo project:
npx create-expo-app my-app
- Copy the source code from your existing codebase into the new Expo project.
- Configure your app’s config (in either
app.json
orapp.config.js
). This can include your app icon and splash screen as well as other settings.- If you’d like, you can use an environment variable (such as
APP_VARIANT
) to set a different bundle ID / package name for development or beta builds of your app. This allows users to install both the live app and the beta build on their device at the same time under different names (e.g. “My App” and “My App (Dev)”).
- If you’d like, you can use an environment variable (such as
- Prepare your list of packages:
- There are a couple options here. For a smaller project, it may make sense to install the latest versions of your packages from scratch so that your project is more up-to-date. For a larger project, that may result in a considerable amount of upgrade work for your packages and you may want to instead ensure that you install the same versions as before.
- In either case, you can use
npx expo install ...
to install your packages. Expo’sinstall
command can help to find a package version that is compatible with your version of Expo.
2. Switch to Expo-compatible libraries
After moving your previous code into the new Expo project, your application likely won’t run yet. For most apps, you’ll have libraries with native code that require additional work to integrate with Expo’s managed workflow.
In some cases, the library may have a config plugin included already. You’ll want to look at the library’s README to see if it has Expo support out-of-the-box. In other cases, you may be able to add a third-party config plugin to support a library.
Expo provides official libraries for common features such as camera use (expo-image-picker
) or video playback (expo-av
) that work smoothly within Expo and may provide a good replacement to libraries used previously. Expo also provides Expo Router, which builds on React Navigation to provide features like file-based routing and deep linking. If you’re already using React Navigation, it’s worth considering the advantages from moving to Expo’s routing solution.
3. Navigate any necessary library upgrades
Now that you’ve switched to supported native libraries, you may also need to address breaking changes in any libraries that you upgraded. This may include an upgrade to a new React Native version as well. Depending on the changes in React Native, you may have a variety of packages to upgrade.
There’s no universal method here, but reading release notes and upgrade guides will provide the best starting point. For example, larger packages may include upgrade guides when moving up a major version that are helpful in making the necessary changes. For smaller updates, changelog entries can be a useful starting place to understand what has changed.
I suggest you work one library at a time, and address errors and issues within the app as they arise. This can be a large undertaking and it can be helpful to tackle a single piece of the architecture at a time (say, navigation or data fetching) as you work through your dependencies.
4. Configure Expo Application Services
While you’re working on getting your app building and running smoothly in Expo, you can also start to take advantage of the capabilities provided by Expo Application Services. For small projects, you can likely gain a number of benefits even with a free plan.
EAS Build provides cloud-hosted servers to prepare your Expo project for distribution to test users or to the app stores. In some cases, this can allow developers to skip creating builds locally with XCode or Android Studio for development. Instead, EAS Build compiles the native code and packages the app releases. These builds can be configured in a myriad of ways, including for local development against a Javascript bundler, for adhoc distribution to a few internal users, or for submission to the app stores for wider testing and production release.
EAS Submit picks up where EAS Build leaves off. By configuring your app store credentials, EAS Submit can automatically submit your builds to the app stores to prepare for release. When set up, your builds can be submitted without any manual intervention, allowing you to go from local development to a TestFlight or internal testing release with a single CLI command.
EAS Update and Push then come into play for supporting your released app. EAS Update makes over-the-air updates of your Javascript code simple. For changes that don’t require updates to native code, you can easily deploy updates to live users without needing to release a new build in the app stores. Expo’s Push notification service provides a helpful abstraction for push notifications to different platforms and simplifies the integration with your back-end server.
These services are a large advantage of the Expo ecosystem and most apps will find significant value in making use of them.
5. QA / testing / validation
Lastly, and perhaps most importantly, after migrating your app to Expo, you’ll want to run through quality assurance and testing for your app changes. Even if the app’s UI hasn’t changed much, there are likely a number of internal components that were modified along the way.
Things to consider include:
- Navigation headers
- Animations
- Device features such as permissions requests and location services
Those may have been impacted by the upgrade.
Since your native modules are configured differently in Expo’s managed workflow, ensure that those are installed and function well. You’ll also want to test out any integrations with external services to see that they work as expected.
Ensure to test on different device screen sizes as the UI may vary from a smaller to larger screen and any notch can impact the available space. Also, make sure to walk through your app’s functionality on both iOS and Android as they have different UI patterns and various other differences that can result in different behavior.
You’ll want to validate your app on physical devices with a production build (often through TestFlight or Google Play’s internal testing). This provides an environment closer to that of your live users and helps to ensure that everything looks and works as expected within the live release.
Welcome to Expo!
It can be a long journey, but Expo’s benefits can make the migration worth it. Expo provides valuable documentation for future upgrades and the tight integration in their ecosystem and the tools in EAS are a great help.
Now that you have your app in Expo, if you’re curious how to make the most of developer tooling within VSCode, check out Trenton’s article on setting up Typescript, Prettier, and ESLint for an Expo project!