Anthony Wentzel

The React Native AndroidX migration week

React Native 0.60 moved Android to AndroidX and half our native modules stopped building. What the AndroidX migration taught me about owning dependencies.

From 2019 · 2 min read · Published September 15, 2026

React Native 0.60 shipped in July 2019 with two big changes: autolinking, and the move from Android's old support libraries to AndroidX. For an app with a lot of native modules, the AndroidX migration was a wall. Every library that still imported android.support.* or used deprecated Gradle configurations broke the build.

Our app at Freebird had a lot of native modules: maps, push notifications, payments, image caching, gesture-driven views, Uber login, and more. Many of them hadn't been updated yet.

The AndroidX migration: fork, patch, pin

Between July 9 and July 11, 2019, I patched our forks of react-native-maps, the community edition of react-native-push-notification, react-native-interactable, tipsi-stripe, react-native-fast-image, and our own react-native-uber-sso. The commit messages are not poetry: feat: androidx, fix: androidx, fix: android x, fix: rn 0.60.

Most of the changes were mechanical. The deprecated Gradle configurations had to go:

lib/android/build.gradle
dependencies {
-    compile fileTree(dir: 'libs', include: ['*.jar'])
-    testCompile 'junit:junit:4.12'
-    compile 'com.facebook.react:react-native:+'
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    testImplementation 'junit:junit:4.12'
+    implementation 'com.facebook.react:react-native:+'
}

And support library imports moved to their AndroidX equivalents:

- import android.support.annotation.NonNull;
+ import androidx.annotation.NonNull;

Then the app pointed at the patched code by git reference instead of an npm release (something like "react-native-uber-sso": "FreebirdRides/react-native-uber-sso#<sha>"), so we could ship on 0.60 without waiting for every maintainer.

What it taught me

You already own your dependencies. The moment a native module is in your app, its build problems are your build problems. Forking just makes that ownership explicit. Once we'd done it, a blocked upgrade became a few hours of work instead of an open-ended wait.

Forks are debt with interest. Every fork has to be rebased when upstream moves, and it's easy to stay on a fork long after upstream has shipped the same fix. If you fork, write down why and when to stop, right next to the dependency.

Mechanical migrations deserve mechanical tools. The community eventually shipped jetifier, which rewrote support library imports inside node_modules automatically. For the bulk of libraries, that was the right fix. Hand-patching made sense for the handful with real code changes.

How I'd do it now

The pattern is the same, but the tools are better:

  • patch-package for small fixes, so the patch lives in your repo as a diff instead of in a fork you have to maintain.
  • Expo config plugins and prebuild, which move a lot of native configuration out of hand-edited files and into code that survives upgrades.
  • A sandbox app on the new version before touching production. I wrote about that habit separately.

Related: Wrapping a native SDK in a React Native native module · Upgrade Expo SDK safely: reproduce it in a sandbox first