Incorporating React Native into your organisation can be enormously beneficial. It can reduce your time to market because you can build once and deploy for many platforms. It can save you money from only needing to maintain one main codebase. It can reduce fragmentation, boost consistency and even improve product reliability and developer experience, if done right.
Many organisations make React Native adoption a cornerstone of their transition to being a modern digital organisation . Thousands of apps already use React Native in production, including many Fortune 500 companies and dozens of household names. What links Microsoft Office and Messenger? Or Playstation and Pinterest, Baidu and Bloomberg, Wix and Walmart? They all use React Native.
But migrating complex apps to React Native is, well, complex. It requires serious planning, world-class expertise and genuine commitment. Organisations almost always need expert guidance and support. At NearForm, our clients report reductions in time to market of up to 40% thanks to our cross-platform app development expertise . But success isn’t guaranteed. Many companies have tried and struggled or failed, and migrating complex existing apps is especially challenging.
We have created a series of articles to outline some of the key principles and pitfalls a company should know about to get it right:
Let’s get started, with part I: planning .
Cross-platform mobile app technology like React Native can greatly increase productivity and product consistency, but for companies with one or more established, complex, real-world-proven apps that have been through years of refinement and iteration, the idea of starting from scratch can be daunting.
A ‘greenfield’ app rebuild (starting from scratch in the new technology while the live app is given essential maintenance only) is the simplest and most efficient way to work, but some organisations see the timelines to reach production as prohibitory. Nothing can go live until everything can go live: all existing features need to be recreated and given rigorous quality assurance testing, to ensure that the clean and slick new app doesn’t lack subtle but crucial details that evolved over time in the old app.
Greenfield projects are much more technically straightforward, and are the clear best option if existing apps are simple, or have significant quality issues such that there’s little risk of losing wheat with the chaff, or if there’s little time pressure to get new features into production. The specific challenges focused on in this series are generally unique to the more challenging brownfield app projects.
A popular way to bring new technology to market faster is a ‘brownfield’ app, where React Native is integrated into the existing app. The team can then build and ship new features in React Native that sit alongside existing ‘legacy’ features that remain largely unchanged until it’s their turn to be modernised and replaced, one at a time. It’s easy to see why this is so tempting:
React Native is the natural choice for such a project, because, unlike most alternatives, the actual app screens are made of the same real native elements as everything else in your app.
React Native’s cross-platform brain is designed to sit in and drive a regular native app. This distinguishes it from platforms like Flutter and Xamarin that bypass the conventional app structure, and ‘hybrid apps’ like Ionic and Cordova that imitate native apps from inside noticeably-different HTML web views.
Some people can be confused by React’s popularity on the web into thinking that React Native apps are also HTML ‘hybrid apps’. They’re not. React Native’s controller follows the same patterns as React, which brings the additional benefit that many layouts, logic, components and features can be made reusable for the web, but within an app, React Native uses 100% real app elements.
A high-quality, well-planned brownfield React Native app can therefore be almost seamless. It’s possible to make a user’s transition from a legacy app feature to one driven by React Native as imperceptible as when a pilot hands over to their copilot.
However, we need to really emphasise those caveats: “high-quality” AND “well-planned” . Making a brownfield React Native migration work well is a very significant technical and organisational challenge, and one with many potential pitfalls to anticipate and avoid.
Before planning the technical engineering work, a number of things should be in place or planned for to make it possible.
A common phrase you’ll hear from people who have worked on brownfield React Native projects is, “It gets worse before it gets better”. This can be moderated to some extent with careful planning. However, there’s no getting around the fact that, by their very nature, these projects aim for long-term efficiency gains via short-term complexity pains. It’s not as simple as it might look
React Native’s documentation for integrating existing apps looks straightforward. The steps appear easy to follow, and they fit the general pattern of wiring in a module or SDK much like any other. It all looks easy… until very suddenly it isn’t.
React Native makes a number of assumptions about the app it sits inside: in particular, that it is both modern (but not bleeding-edge modern) and simple (but with certain expected configurations). However, real-life ‘brownfield’ React Native projects involve complex apps with years of accumulated technical debt that may have taken less conventional paths. If the apps were as simple as assumed, they could easily be rewritten as ‘greenfield’ apps, and wouldn’t need this intermediate step where pure native and React Native coexist. Expect the unexpected React Native’s requirements aren’t very strict, and some projects may get lucky and encounter almost no friction here, but it is likely that there will be more unexpected issues, delays, false starts and required compromises than in a typical software project.
It’s also almost certain that the developers who need to resolve these issues will encounter them while working outside of their normal comfort zones and core skill sets.
The people challenges characteristic of brownfield project may be even more tricky and delicate than the technical challenges: you’ll rely on rare skills intersections including the in-house expertise of the people who, if you’re not careful, may be the most disrupted and alienated by the project. You’ll need expertise spanning your apps and React Native internals The most difficult work in the migration process will be wiring in React Native to areas of your existing apps that are complex, idiosyncratic or unconventional. Resolving these will require above-average expertise on both sides of the intersection, but you can’t magic up one person who is an expert on the quirks of your Android app and your iOS app and React Native at the same time.
Find internal experts who are able and willing to learn React Native, and pair them with experienced React Native developers or consultants who have baseline skills in native code. Writing React Native code is very different to writing Android and iOS code Learning React Native will require the people in your in-house team to step out of their comfort zones. It’s not just different languages, the whole approach is different (functional vs object-oriented, interpreted vs compiled). Well-planned training and good technical leadership will be crucial.
In particular, reduce culture shock by investing some time in TypeScript and IDE configurations that are comfortable for developers used to strict type safety within purpose-built IDEs, and boost motivation with some focus on areas where the React Native developer experience is an improvement on native apps, such as:
Developers will have personal concerns It’s only natural that some individuals may see change and new technology as a threat, and this needs to be carefully managed on a personal level. Some may also struggle with the change from feeling like a big fish in a small pond, making large contributions to one app, to feeling like a smaller part of a bigger team. The team overall will be more productive as they build for multiple platforms, but individuals may feel like a smaller part of it.
Be patient, and be prepared for pushback and growing pains. Sometimes, some code can and should stay native The existing native apps won’t be simply junked and your team’s existing skills and knowledge won’t become wholly obsolete: as well as the lengthy transition period, some elements of existing apps may be uniquely complex, sophisticated or platform-specific, and one of the benefits of React Native over rival cross-platform systems is that these can be repackaged as native modules. This unique, battle-tested code can continue in its current form, as a platform-optimised component of a cross-platform app.
Identify if there are any such areas in your apps, and ensure that necessary skills and knowledge are retained.
Robust testing and quality assurance is always important in any app project. Hopefully, the apps that are going to be migrated already have robust QA processes and automated test suites, including end-to-end testing of a complete build — but often, React Native migrations are part of digital modernisation programmes where the existing apps haven’t followed best practices. Smoke tests At a bare minimum, you’ll need automated smoke tests that catch unexpected breakages in key user journeys and features, especially any features that depend on other integrations like third-party SDKs. Adding React Native shouldn’t compromise or change any existing features, but it has certain build configuration requirements, and changing build configuration always carries a small risk of unexpected side effects. It’s important to know that if something did change or break in such an unexpected way, this would be caught.
It’s also important to ensure a wide range of devices are tested with at least ‘does the app start’ smoke tests. React Native loads some device-specific binaries at run time, meaning that for non-standard integrations, there’s a slim risk of device-specific crashes if a misconfiguration caused a binary needed by an unusual device type to be missing.
At a bare minimum, set up a basic QA system, including automated and manual testing, where every major feature is tested on a wide and market-appropriate range of devices before each release. End-to-end tests Migrating features to new technology naturally brings a much higher risk of regressions. E2E tools that run on a complete app build, like Appium and Maestro, can be used equally well for React Native and pure native features. It is extremely advisable to fill in any gaps in E2E tests for each feature or screen before it is migrated: if the migration is done well, these tests should pass with no changes needed except for planned refinements or modernisation, confirming that the core behaviours and interactions have remained the same.
If possible, have a robust CI/CD pipeline running E2E tests, where each screen and feature has comprehensive coverage before beginning its migration. Tools specific to React Native Features created in React Native can benefit both from this existing E2E tooling, and more focused dedicated tools designed for React Native. These include linters that catch mistakes and anti-patterns as they’re written, and unit tests and component snapshot tests that can catch errors and regressions in logic or layout before code changes are even committed, without needing a build.
Set up tools like Husky, React Native Testing Library, and ESLint with plugins for React Native, React hooks and React Native accessibility from the start.
There are lots of different ways mixing React Native and native screens in an app can be managed, but they mainly boil down to three:
This is probably the best long-term option. This Shopify case study describes in detail how this strategy can successfully work at scale. In summary:
This is a quicker but dirtier option — it’s faster to add visible value, but accumulates more complexity and has many potential pitfalls. CallStack / Kiwi.com have a case study discussing mitigating some of those pitfalls. In summary:
This is quite simply the worst of both worlds. A well-planned project shouldn’t use this strategy, but there’s a risk of ending up here by accident if the React Native documentation is followed as written without proper planning. In summary:
Option 1 (React Native shell) is an advisable long-term option. This is because it provides a clear, scalable foundation where each new migration of a screen or feature to React Native reduces the complexity and technical debt on the native side. It shows a clear pathway for migrating to React Native long term, and once the React Native shell architecture is built, each migration reduces overall complexity and technical debt. Option 2 (Native shell, React Native fragments) is an advisable option for short-term projects. These include ‘Proof of Concept’ (PoC) apps not intended for production, or legacy apps approaching end-of-life where only a small number of React Native features are intended to be added or where there isn’t a plan to migrate the rest of the app.
It is the fastest way to ship a working React Native feature, with fewer changes (therefore fewer possible regressions) to the native app. However, it is not an ideal pathway for long-term complete migration of an app to React Native. This is because each new React Native screen and feature accumulates complexity and technical debt on both the native and React Native side.
One strength it does have, is that a PoC app produced via option 2 can be a powerful tool for demonstrating the speed and power of React Native, which can be valuable for getting buy-in for a bigger project from unsure stakeholders. While the PoC itself isn’t an ideal foundation for further development, its build configuration will be reusable, and this is one of the most difficult parts of a brownfield React Native project, so effort is not wasted even if the specific PoC features are never pushed to production.
Here are a couple of possible mixed strategies for getting the best of both worlds:
This is a medium-risk and medium-pace option that may be advisable if:
Process: 1: Start with a Proof of Concept following the ‘Option 2: Native shell, React Native fragments’ approach, with a clear mutual understanding that:
2: Use the working Proof of Concept app as a learning and internal communication tool. Demonstrate to stakeholders how quickly and easily feature changes can be made within the React Native feature, and use it as a training tool to kickstart React Native upskilling of native developers
3: Build an ‘Option 1’ React Native shell on top of the working ‘Option 2’ build configuration and project, rebuilding the PoC React Native feature as part of what is now a React Native app where native legacy screens are managed by React Native navigation.
4: Once the shell is built and regression-tested, the team is ready to focus on bringing new features to production built on React Native, with the foundations necessary to ensure that the difference can be made largely invisible to the user Strengths:
This alternative takes several small risks to pull some of the React Native frontend work earlier in the project. It is worth considering if:
Process: 1. Set up a pure React Native app, where the React-oriented team members can build the main app shell and navigation, then start working on those aspects of the high-priority new features that have few dependencies on the native app.
2. Meanwhile, in parallel, the native-oriented side of the team works on build configuration and integrating a minimal React Native “hello world” screen into the native app, with nothing added beyond a decision on whether to use “New Architecture” with Hermes and Fabric (more on that in Part II ), and bare installs of required React Native modules that have native-side requirements: typically this may include
react-native-svg , a navigation library (for brownfield projects, the more native-oriented
react-native-navigation is likely a better choice than the more popular
@react-navigation ), and any React Native wrapper modules for business-specific SDKs you use.
3. As soon as the native apps are building successfully with React Native integrated, the native-oriented team pivot towards merging the workstreams: weaving the existing native screens, services and API into the new React Native shell Strengths:
Adding React Native to an existing, complex app can yield excellent benefits and results for businesses, developers and users. However, while the rewards are significant, undertaking and achieving this presents a significant challenge to technical and management teams that should not be underestimated.
Careful planning is essential, as is ensuring that experts are available who can handle the technical challenges. This is something NearForm can help you with. If you’d like to discuss how we can support your organisation in adopting React Native then get in touch . Our experts would love to chat about how we can help.
In part II , we will look at some of the specific technical challenges that come with adding React Native to a complex app.