Why I Migrated My Bare React Native App to Expo (And What Most Guides Don’t Tell You)
A real-world journey of transforming a legacy React Native app into a streamlined Expo project, faster, leaner, and future-ready.

In less than a week, I converted a legacy React Native application that was more than six years old to Expo.
Without starting from scratch, I managed in-app purchases, moved to a more contemporary CI/CD setup with EAS, removed more than 150k lines of native code, and even managed native widgets.
More significantly, though, I discovered topics that no one else discusses.
Why I Finally Switched to Expo
I didn’t jump into Expo because it was trendy, I was resistant for years. I liked having full control over my native folders and scripts. But in 2025, Expo isn’t just for beginners. It’s evolving into the go-to ecosystem for serious, production-grade apps, even ones with complex native integrations.
Here’s why I switched:
It is now evident and strategic that React Native is officially moving toward frameworks (such as Expo).
My hand was forced by CodePush sunset.
I have had enough of native configuration juggling and "robotic" upgrades.
Native setup is almost plug-and-play thanks to Expo's CNG (Continuous Native Generation) + configuration plugins.
Most importantly: I wanted less maintenance, not more.
What Most Blogs Don’t Cover: Pre-Migration Strategy
The strategic planning stage is typically omitted from migration blogs. This is the crucial preparation I completed prior to issuing a single Expo command:
1. Audited Native Dependencies with Intent
I did more than just look for configuration plugins in packages. I inquired:
- Is this package still in compliance with the most recent RN standards?
- Is there a Web API or universal Expo package that can take its place?
- Is it archived or maintained?
Underappreciated Tip: If you're developing hybrid or web-first applications, you can use contemporary Web APIs in place of some native packages (through Expo Router + PWA support). This significantly lowers the complexity and build size.
2. CI/CD Planning Before Coding
EAS is a robust solution, but it isn't a plug-and-play solution for migrations. I created a map:
Which login information I required for iOS and Android
distinct app versions for production, staging, and development
Managing secrets in various contexts
➡ Pro Tip: Route secrets automatically at build time by using Expo's extra configuration key and.env.eas files.
After reducing native complexity with Expo, I focused on improving performance. If you're doing the same, check out these performance strategies for React Native apps in 2025.
The Actual Migration Steps (With 2025 Context)
Step 1: Install Expo + Modules
bash
CopyEdit
npx install-expo-modules@latest
By doing this, you can get your app "Expo-ready" without starting a new project. I didn't move any JS files to a different repository. Rather, I iteratively changed my repository while maintaining it.
Step 2: Native Code = Plugin Puzzle
You have three choices if your package relies on native setup and lacks a configuration plugin:
- Look for a maintained plugin on GitHub or NPM.
- Writing your own configuration plugin is no longer that difficult!
- Change the reliance
➡ Undiscovered Treasure: You can now wrap native Swift/Kotlin code using the Expo Modules API and utilize it just like any JavaScript module without having to eject it.
Step 3: Adopt CNG the Smart Way
bash
CopyEdit
npx expo prebuild --clean
Prior to running this:
- Make a backup of everything.
- Remove sensitive files from the project, such as key store.
- Execute Expo Doctor and Expo Install --check.
➡ 2025 Bonus: You can now support dynamic plugin loading per environment by modularizing your prebuilt plugins.
Widgets, Notifications & Native Extensions in 2025
An iOS widget and push notification extension (OneSignal) are two of my app's primary features.
What Functioned:
Evan Bacon used the expo-apple-targets plugin.
With a minor patch, localized widget support was added.
To prevent configuration conflicts, I made sure the plugins loaded in the correct order.
What I Was Not Warned About:
Expo plugins operate one after the other. You risk silently breaking builds if one overrides another (for example, widget plugin vs. OneSignal). The sole remedy? Rearrange the array of plugins.
➡ Unusual Solution: Using environment variables, I created a dynamic plugin resolver that conditionally loads incompatible plugins according to build type. saved me hours of troubleshooting.
What Expo Automates Too Much
To be honest, Expo does a lot, but not always in a transparent manner.
Peer dependencies that are automatically installed occasionally clash with custom configurations.
If you don't routinely check the output, generated native folders may conceal minor errors.
Expo logs don't always show widget or extension problems; instead, look directly at Xcode.
Final Thoughts: Would I Do It Again?
Of course. The advantages exceed the drawbacks:
- CI/CD that simplify the functions
- More than 150k lines of native code were removed.
- Updates and dependency management are made easier.
- Current build variations that support native extensions
Though: this migration isn't easy for beginners.
You can hire React Native developers with experience in CI/CD automation and Expo migrations might be a good option if your team lacks the necessary time or skills. It involves more than just writing code; it involves creating a solid basis for future development.
About the Creator
Julie Grant
I write about emerging tech, mobile app development, e-commerce trends, and digital growth strategies. Whether it's educating readers or sparking conversation, I aim to deliver content that informs, inspires, and converts.



Comments
There are no comments for this story
Be the first to respond and start the conversation.