A native dependency is free to install and billed at every release after that.
The cost of a native dependency is not money and it is not app size. It is that three things stop being free: shipping a change, upgrading the project, and leaving the native directories alone. One command installs it. From then on the library is compiled into your binary, which means the version your users have is the version that was built, and every change to it needs a new build. That is the whole bill, and it is worth knowing before you install rather than after. This page assumes you already know what React Native is and that somebody handed you a project built with it.
What follows is how to tell whether the package you are about to add has native code in it, what it changes about the build, what it changes about the directories you do not own, and the cost almost nobody prices in: a native dependency is a vote on whether you can upgrade next year. Every mechanism below comes from React Native and Expo documentation read on 17 October 2026.
Check the package you just addedThe short version
Two kinds of dependency, and only one of them is cheap.
A JavaScript-only package is text your app reads. Install it, reload, done. A package with native code is source that has to be compiled into the app on each platform, and the compiler is not running on your users' phones. React Native's own guide makes the split plainly: it installs a library, then says the library includes native code and has to be linked before you use it, with CocoaPods on iOS and Gradle on Android, followed by a rebuild of the app binary each time.
Nothing about that is a reason to avoid native dependencies. Maps, payments, camera access, secure storage and push notifications all arrive this way, and a project with none of them is a project that does very little. The point is that the two kinds of install look identical when you type them and behave nothing alike afterwards, so the decision deserves thirty seconds of checking rather than none.
How do you tell whether what you installed has native code?
You look, and there are four places to look. Expo's guide to using libraries, read on 17 October 2026, gives the test as four questions. Does the library include android or ios directories? Does its README mention linking? Does it require you to change AndroidManifest.xml, the iOS Podfile or Info.plist? Does it have a config plugin? Expo's own instruction is that a yes to any of those means you should create a development build to use the library. That is the cost signal. One tick is enough.
The faster version is React Native Directory, which exists for this and carries the compatibility facts per library, including a tag for whether a package works inside the Expo Go app. React Native's guide adds three more questions worth asking before any install. Does it work with React Native at all, since the npm registry happily serves packages written for browsers or for Node, and those will not run here. Does it work on the platforms your app ships on. And does it work with the version of React Native you are actually on, because the latest release of a library is usually matched to the latest release of React Native rather than to yours.
That last question is the reason to stop typing npm install in an Expo project. Expo recommends always using its own install command instead, because the CLI then picks a version of the library that is compatible with your project and warns you about known incompatibilities. npm takes the newest published version and tells you nothing. For a JavaScript package that mistake surfaces as a readable error. For a native one it surfaces as a failure deep in a platform toolchain, and you will spend an afternoon there.
React Native, Using Libraries, last updated 12 August 2026, read 17 October 2026
Check the package
Four signals, and what each one costs you
These are the four questions Expo tells you to ask before installing a third-party library. Tick the ones that are true of yours.
Nothing ticked. On Expo's own test that is a JavaScript-only package, and it costs you nothing beyond its own bugs. Install it, reload, carry on.
What does it change about the build?
It makes the build a step you take, rather than a thing that happened once. React Native's instructions for a library with native code are the same shape on both platforms. On iOS the project uses CocoaPods, so you run pod install, or npx pod-install from the project root, and then re-build the app binary before you can use the library. On Android the project uses Gradle, and after installing a library with native dependencies you re-build the binary again. There is no reload that substitutes for either.
Two consequences follow that people meet in the wrong order. The first is that everyone on the project needs that new build, not just you. The person testing on their own phone is running whatever binary they installed last week, which does not contain your new library, and the error they report will look like a bug in your code. Hand out the new build first and the bug reports stop. The second is that build minutes are now attached to dependency decisions. A JavaScript-only change can be tested in seconds. Adding a native package means waiting for two platform builds before you know whether it was the right package.
And it changes what reaches your users without a store release. Native code lives in the compiled app, so the version of the library on somebody's phone is the version that was built into what they installed. JavaScript fixes can still travel between releases. A new native dependency, a bumped one with changed native code, or a removed one cannot. On iOS that means another pass through review before anybody has it. If you have been fixing things the quick way, a native dependency is what takes that away for one release.
React Native, on linking native code on iOS and Android, read 17 October 2026
What does it change about the project itself?
Less than you would fear, as long as you never hand-edit the native directories. Expo's continuous native generation documentation, last modified 25 September 2026, describes the arrangement: the android and ios directories are generated from a template for your SDK version plus your configuration, and autolinking wires in the native modules it finds in package.json. Installing a native package is therefore usually a one line change to package.json, and the native plumbing is derived rather than written. That is why the directories are added to .gitignore on a new project and regenerated when needed.
The expensive case is a library that needs configuration as well as code. Expo's own example is a camera library, which needs permission strings added to AndroidManifest.xml on Android and Info.plist on iOS. If the author shipped a config plugin, that configuration is a versioned, testable script and it survives regeneration. If they did not, you either write a plugin, take one from the community list Expo points at, or paste the native configuration in by hand. Expo is blunt about the third option: it can lead to difficult native compilation errors, and it is code you now own and maintain. Worse, it quietly disappears, because modifying the generated directories risks losing the change the next time they are regenerated from clean.
Two smaller costs are worth naming because both arrive late. The first is orphaned configuration. When you uninstall a package you have to be sure you removed every side effect it needed, and Expo's point is that untraceable leftovers accumulate and make the project harder to understand. A config plugin fails loudly when its package is gone. A pasted edit in a generated file does not. The second is the moment someone commits the generated directories. Expo says the build service generates them before compilation when they are absent, and that for a project which already has them it will not run generation at all, specifically to avoid overwriting your changes. That is a sensible rule with a sharp edge: the commit that adds those directories is the commit that makes their contents your problem forever.
None of this depends on who typed the project. It is worth being clear about that, because the question of who owns AI generated code and the question of what a dependency costs are separate. A library with native code behaves the same way in a project written by a contractor, a founder or an agent. The package decides, not the author.
Expo, continuous native generation, modified 25 September 2026, read 17 October 2026
Why is every native dependency a vote on next year's upgrade?
Because the generation step starts from a template tied to your SDK version, and that template comes with specific React and React Native versions. Upgrading means bumping the SDK, bringing every dependency into line with it, and regenerating the native directories. Expo gives the sequence in its upgrade guide: install the new expo package, run its install command with the fix flag to align dependencies, run its doctor command to catch common problems, then delete the android and ios directories so they are rebuilt for the new version. For a project of JavaScript packages that is the whole job.
Each native dependency adds a condition to that job. The package has to have a version that works with the SDK you are moving to, and you do not control whether it does. A popular library will. One that three people use and nobody has touched in two years may not, and then the choice is to drop the feature, pay someone to update the package, or stay on the old SDK and watch the rest of the ecosystem move. Expo cites the 2022 React Native survey, reporting that developers who do not use generated native projects named upgrading as the framework's number one weakness, and generation is its answer to exactly that. The answer works. It just cannot make an abandoned package compatible.
So the practical rule is to count them, and to count the native ones separately. A project with four native dependencies is waiting on four maintainers. A project with twenty five is waiting on the slowest of twenty five, and you find out which one that is on the day you try to move. Each one you add is a small, permanent reduction in how freely you can move, which is a fair price for maps and an absurd price for a loading spinner. Before any native install, read the Expo SDK release notes for the version you are on, because breaking changes are listed per release and that is where you find out a package you depend on was deprecated.
There is one escape hatch, and it is honest about being a hatch. If you need to pin a specific version of a third-party library and stop the tooling complaining, Expo documents an exclude list in package.json that takes a package out of the version checks run by its install, doctor and start commands. That silences the warning. It does not make the pin safe, and the next upgrade will find it again.
Expo, on how generated native projects change upgrades, read 17 October 2026
The same install, three different bills
Read down the column that matches the package in front of you. The middle column is the normal, healthy case and the right hand column is the one that costs you something every time.
| What it costs | JavaScript only | Native code, config plugin | Native code, no plugin |
|---|---|---|---|
| Runs in Expo Go | Yes | Only if that runtime already bundles it | Only if that runtime already bundles it |
| Needs a new development build | No | Yes | Yes |
| Linked for you from package.json | Nothing to link | Yes | Yes |
| Native configuration survives a clean regeneration | Nothing to configure | Yes | No |
| Reaches users without a new binary | Yes | No | No |
| Can block your next SDK upgrade | Rarely | Yes | Yes |
| Leaves traceable config behind when removed | Nothing to leave | Yes | No |
Where this lands if an agent installed the dependency
Newly is an AI app builder. You describe an app in plain English and it writes a real React Native and Expo project you own, on effort-based credits from $25 a month with no free plan. Installing libraries is part of what the agent does, so the dependency list in your project was partly chosen for you. That is the normal condition of anyone handed a codebase, and the useful question is not who typed the install but whether you can see it and whether you can get out.
On both counts the output is ordinary, which is the point. The project has two way GitHub sync, so every dependency is a readable line in package.json and a diff you can review like any other. The build path a native dependency requires is already wired up: iOS builds upload to TestFlight, Android publishes to Google Play internal testing, and a standalone APK comes out too. Since native code can only reach a person through a build, having that path working before you need it is worth more here than almost anywhere else. People arriving at this from building React Native apps without coding usually meet the native dependency question the first time they ask for maps or payments.
Two limits, stated so they are not a surprise. Newly does not submit to the App Store for you, because Apple requires the owner of the app's content to submit it, so the release that carries your new native dependency is a release you press send on. And no builder, no prompt and no subscription makes an unmaintained native package work with next year's SDK. Generated or hand-written, that cost is attached to the package. The only control you ever have over it is the decision you make before you install.
Questions people ask about native dependencies
Expo's guide to using libraries gives four questions, and a yes to any one of them means the library touches your native project. Does the package include android or ios directories? Does its README mention linking? Does it ask you to change android/app/src/main/AndroidManifest.xml, ios/Podfile or ios/Info.plist? Does it ship a config plugin? Expo's instruction after those four questions is to create a development build to use the library. React Native Directory is the faster version of the same check, because it carries platform and Expo Go tags per library.
Count the native ones, then build
Open package.json and work out which lines have native code behind them. That number, not the total, is what decides how quickly you can ship a fix and how painful your next upgrade is going to be.
Start building