Adding a Native Dependency

Install a library with native code: the Expo-compatible question, autolinking, pods and Gradle, and reading the build error.

Syntaxnpx expo install <package> && npx expo prebuild --clean && npx expo run:ios

A library with native code is not just an npm install. Its Swift, Kotlin or C++ has to be compiled into your app, which means a new native build and, if you use Expo Go, the end of using Expo Go.

Before installing

Check reactnative.directory, then the README. Three questions decide everything: does the package contain an ios/ or android/ folder, does it ask you to edit Info.plist, AndroidManifest.xml or the Podfile, and does it ship a config plugin. Any yes means native code, so you need a development build rather than Expo Go.

Install with npx expo install <package> rather than npm or yarn directly. It picks a version compatible with your Expo SDK and warns about known conflicts. npx expo install --check reports mismatches, --fix corrects them, and npx expo-doctor audits the whole project.

Autolinking

You almost never link by hand. Expo's autolinking discovers Expo modules through their expo-module.config.json and React Native libraries through the community CLI convention, then registers them during the native build. On iOS that still means CocoaPods and a pod install step, run for you by expo prebuild or npx pod-install; React Native 0.87 adds experimental Swift Package Manager support as an alternative. On Android, Gradle resolves the discovered projects.

Reading the error

Native build failures are long. Read from the bottom for the first line naming a file in your project or in node_modules; everything above it is usually noise.

Example

Example · tsx
// App.tsx
// Installed with: npx expo install @react-native-community/netinfo
// Then rebuilt with: npx expo run:ios
import { useNetInfo } from "@react-native-community/netinfo";
import { StyleSheet, Text, View } from "react-native";

export default function App() {
  // The hook is JavaScript. The reachability check behind it is native code
  // that autolinking compiled into the iOS and Android projects for us -
  // we never opened Xcode or touched settings.gradle.
  const { isConnected, type } = useNetInfo();

  // isConnected is boolean | null: null means "not determined yet", which is
  // not the same as offline. Treating null as false shows a false alarm on
  // the first frame.
  const label =
    isConnected === null ? "checking" : isConnected ? "online" : "offline";

  return (
    <View style={styles.container}>
      <Text style={styles.status}>{label}</Text>
      <Text style={styles.detail}>connection type: {type}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: "center", justifyContent: "center", gap: 8 },
  status: { fontSize: 28, fontWeight: "600" },
  detail: { fontSize: 14, color: "#57606a" },
});

When to use it

  • Add a maps, Bluetooth or in-app purchase library and get it compiling on both platforms.
  • Diagnose a "null is not an object" crash on a native module that was installed but never rebuilt.
  • Decide whether a promising-looking library is safe to adopt, or unmaintained and about to block your next upgrade.

More examples

The install sequence, with the checks

The order is what matters. Installing and then reloading the app is the most common mistake: the JavaScript is there, the native half is not, and the error reads as if the package were missing. Run this sequence once per native dependency. In CI, EAS Build performs steps 3 and 4 for you, which is why a library can work on your machine and fail in CI if you committed a stale ios folder.

Example · bash
# 1. Install a version matched to the Expo SDK, not just the newest on npm.
npx expo install @react-native-community/netinfo

# 2. Confirm nothing in the project drifted out of range.
npx expo install --check      # report mismatches
npx expo install --fix        # move them onto compatible versions
npx expo-doctor               # wider audit: duplicates, bad config, stale plugins

# 3. Regenerate the native projects so autolinking sees the new package.
npx expo prebuild --clean

# 4. Build and install. This step compiles the library's native code.
npx expo run:ios
npx expo run:android

# If you keep the ios folder checked in rather than generating it, the pod
# step is the one that must not be skipped:
npx pod-install

Opting a dependency out of autolinking

Reach for this when two libraries register the same native module and the build fails on a duplicate symbol, or when a transitive dependency drags in native code you do not use and you want it out of the binary. Setting a platform to null tells the CLI to leave that side alone; the Expo exclude list does the same for Expo modules and is also used to trim binary size.

Example · js
// react-native.config.js
// Autolinking is all-or-nothing per platform. Use this when a library ships
// native code for a platform you do not build, or when you link it manually.
module.exports = {
  dependencies: {
    "some-legacy-library": {
      platforms: {
        ios: null, // do not autolink on iOS
      },
    },
  },
};

// For an Expo module, the equivalent lives in package.json instead:
//
// {
//   "expo": {
//     "autolinking": {
//       "android": { "exclude": ["expo-random"] }
//     }
//   }
// }

Reading a native build failure

The skill is knowing which line to read. A Gradle or Xcode failure prints hundreds of lines and the useful one names a file and a line number. A path under node_modules means the library is at fault; a path under your own app means you are. This particular shape — a library failing to compile after an upgrade — is the most common native build error there is, and the fix is almost never in your code.

Example · text
# A real Gradle failure, trimmed. Read from the bottom up.

> Task :react-native-some-lib:compileDebugKotlin FAILED

e: file:///Users/me/app/node_modules/react-native-some-lib/android/src/main/
   java/com/somelib/SomeLibModule.kt:14:32 Unresolved reference: ReactContext

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':react-native-some-lib:compileDebugKotlin'.
> Compilation error. See log for more details

# What this says, in order of usefulness:
#  1. The failing file is in node_modules, not your code.
#  2. The library's Kotlin does not compile against this React Native version.
#  3. So this is a version mismatch, not a mistake in your app.
#
# What to do:
#  - Check the library's README for the React Native version it supports.
#  - Look for a newer release: it may already support 0.87.
#  - React Native 0.87 requires Kotlin 2.0+ and raised compileSdk to 37, so
#    a library pinning an older Kotlin plugin will fail exactly like this.
#  - If nothing works, the library is unmaintained. Choose another one.

Discussion

  • Be the first to comment on this lesson.