A native module is native code someone writes. The question is whether that someone is you.
A native module is the integration code that lets your JavaScript call a platform API React Native does not already provide. React Native's own guide puts it almost that way: your application code may need to interact with native platform APIs that are not provided by React Native or an existing library, and you can write the integration code yourself. So the honest reading of "without writing native code" is not that the native code disappears. It is that somebody else already wrote it and put it on npm.
You probably never have to write one. Expo answers that question in its own documentation: most of the time, Expo and React Native developers do not need to write any native code. Read on 17 October 2026. Cameras, video, maps and haptics are all named as cases already covered by published libraries.
This page is for someone holding a React Native and Expo project they did not type. What a native module actually is, how to tell whether the thing you want needs one, what it costs you the day you add it, and what changes because the project was generated. Every quoted sentence comes from React Native's or Expo's own documentation, read on 17 October 2026.
Start hereThe short version
Having native code and writing native code are two different facts.
Your project is already full of native code. The camera, the file system, notifications, secure storage: each of those is Swift, Kotlin, Objective-C or Java that somebody wrote, published and versioned, and that got compiled into your app when it was built. Installing one of those is not writing a native module. It is installing a library.
The line that matters runs somewhere else. It is not whether native code exists in your project, because it does. It is whether anyone has to open Xcode or Android Studio and write a new line of it. Expo draws the same line in its workflow documentation, as a loop of four activities: write and run JavaScript, update the app config, write native code or modify native project configuration, and install a library that requires native code modifications. Two of those four are free. The other two cost you a build. Only one of them costs you a developer.
What is a native module, exactly?
Three pieces, not one. React Native's current guide says a Turbo Native Module is "both your specification, the native code you write, and the Codegen interfaces generated from your specification". The specification is a typed file in TypeScript or Flow that declares the methods and data types crossing between native code and the JavaScript runtime. Codegen turns that specification into native language interfaces. Then you implement those interfaces in Java or Kotlin on Android and in an Objective-C++ file on iOS.
The reason that structure matters to someone who is not going to write one is the build step. Codegen is not something you run and forget. On Android it runs through a Gradle task that fires automatically when you build. On iOS it runs as part of script phases added to the project by CocoaPods. A native module is therefore wired into compilation, not into the JavaScript bundle. That one fact explains most of its cost, and the rest of this page.
A native module has a sibling that is easy to confuse with it. A module exposes functions you call. A native view, called a Fabric Native Component in React Native's wording and simply a view in Expo's, exposes something you render inside your JSX. Both APIs cover both shapes. If what you want is a platform control sitting in the middle of a screen rather than a function you call from a button handler, you want the view, and the practical cost is the same.
There are two current APIs for building one, and they are not rivals in the way the names suggest. Turbo Native Modules are React Native's own. The Expo Modules API is Expo's, written in Swift and Kotlin, and Expo describes it as the best choice for the vast majority of developers building native modules for their apps. Expo also publishes the dividing line, paraphrasing the React Native team: use Turbo Modules if you intend to use C++, because it gives easier access to lower level mechanisms, and use the Expo Modules API if you want a better developer experience and are willing to depend on the expo package. Both run on React Native's JavaScript Interface rather than the old message queue, and Expo says their performance characteristics are similar.
One more thing a native module is not. It is not a config plugin. Expo defines a config plugin as a top-level custom configuration point that is not built into the app config, and its job is to change what goes into the generated native project: a permission string, an entitlement, a build setting. That is configuration. Nobody writes Swift for it. The two get confused constantly, partly because both are filed under native work and partly because adding either one means building the app again.
Do you need one, or does a library already have it?
Start from the assumption that you do not. React Native's own framing is that you write a module when you need platform APIs that are not provided by React Native or an existing library. Expo states the same position more bluntly and gives the two cases where it stops being true. One: an SDK you have to integrate, usually a vendor your company has mandated, that has no React Native library yet, so somebody has to build a module around it. Two: a system feature your app needs that is uncommon enough that nobody maintains a library for it. Outside those two, the answer is a library.
Checking which case you are in takes about a minute, and Expo publishes the test as four questions about the library you are considering. Does it include android or ios directories? Does its README mention linking? Does it make you change AndroidManifest.xml, or the Podfile, or Info.plist? Does it have a config plugin? A yes to any of them means the library touches the native side, and Expo's answer is that you should create a development build to use it. React Native Directory tags the libraries that work in Expo Go, and you can filter the list by that tag.
Notice what that test is really measuring. It is not asking whether a native module is involved. It is asking whether the native project has to change. Those come apart in both directions: a library can contain a fully fledged native module and still need nothing from you beyond installing it, and a library with no module at all can still force a rebuild because it wants a line in Info.plist. If you are new to the tooling and the vocabulary is the obstacle rather than the decision, getting started with Expo covers the ground this page assumes.
Expo, Expo Modules API overview, read 17 October 2026
Check yours
Does the thing you want need a native module?
Tick what you are trying to add. The routes follow the four activities Expo lists in its own development loop.
Nothing ticked. Four of the six routes below need no new native code from anyone, and two of them need Swift or Kotlin. Tick one to see which.
What does one actually cost you once it is in?
The first cost is the one people expect and it is the smallest. Expo Go stops being an option. Expo's documentation calls it a playground for students and learners, says it does not include all of the native code required to support every library, and says plainly that it is not useful for building production-grade projects. You move to a development build, which contains the native code your particular project needs. Mildly annoying, once.
The second cost is the one that reshapes how you ship. Expo defines a runtime version as a property that guarantees compatibility between a build's native code and an update, and states that a build contains native code that cannot be changed with an update. Read that twice if you ship fixes to users between store releases. It means a native module can never go out over the air. A new module, or a changed one, means a new binary, a bumped runtime version and, on iOS, another trip through review before anybody has it. Expo also warns about the failure mode in the other direction: an update that relies on native code the build it is running on does not support, which expo-updates may catch and roll back. If you have been treating over the air updates as the normal way to fix things, a native module is the thing that takes that away for one release.
The third cost is maintenance, and it never ends. The module is now code in your project that has to survive every React Native and Expo upgrade, and nobody but you will test it. Where it lives decides how well it survives. Expo's documentation is explicit that the generated android and ios directories are added to .gitignore for new projects and can be regenerated at any time, and that manual changes to them risk being lost the next time they are regenerated from a clean state. A module written as a local Expo module, which lives in a modules directory next to package.json, survives that. A hand edit buried in ios/Info.plist does not. That is the practical reason to insist on the module route even when a direct edit would be quicker.
One cost that is usually overstated is weight. Expo says adding the Expo Modules API to an app has a negligible impact on app size, perhaps a few hundred kilobytes, and that its performance characteristics are similar to Turbo Modules because both use the JavaScript Interface rather than the old serialised bridge. So the module machinery is rarely what makes an app slow to open. If your app got worse after a library went in, that is a measurement question about app startup time, not an argument against native modules.
The last cost is money, and it is the one no documentation page will quote you. An hour of Swift is not in any tool's subscription price. Neither is the person who comes back to it in eight months when iOS changes under it. Price that the way you would price any other custom work, alongside what it costs to publish an app, and then check one more time whether a published library already does nine tenths of it.
React Native, Turbo Native Modules, on the Codegen build step, read 17 October 2026
What changes if a builder generated the project?
Less than you would think, and the difference is in one direction only. 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. It runs on effort-based credits from $25 a month with no free plan. What the agent works in is JavaScript, TypeScript and the app config, and it installs libraries: that is the first two activities in Expo's loop, plus the fourth. It is the overwhelming majority of what any app needs, which is the same reason Expo can say most developers never write native code.
The honest limit is the third activity. If the capability you want needs Swift or Kotlin that nobody has published, no prompt makes that appear. A generated project does not change the arithmetic of the two cases Expo names, the mandated vendor SDK and the uncommon system feature. In both, somebody writes native code, and that somebody is a developer you hire or a developer you already have. Any tool claiming otherwise is describing a library it bundles, not a module it invented.
What a generated project does give you is somewhere for that work to go, which is not true of every builder. The output is an ordinary React Native and Expo project with two way GitHub sync, so a contractor can add a module on a branch in the same repository and you keep working in the builder. The build path is already there too: it uploads iOS builds to TestFlight, publishes to Google Play internal testing, and produces a standalone Android APK. Since a native module can only reach people through a build, having that path working before you need it is worth more here than anywhere else.
Two things it does not do, stated so you do not discover them late. It does not submit to the App Store for you, because Apple requires the content owner to submit the app. And it will not write the native half of a module for a vendor SDK that has no React Native package. The useful way to hold both facts is this: the generated project puts you on the normal React Native road rather than off it, so when you finally hit the one thing that needs Swift, the fix is an ordinary piece of contract work on an ordinary codebase, not a migration off a platform.
Expo, on when nothing does exactly what you need, read 17 October 2026
Questions people ask about native modules
It is the integration code that lets your JavaScript call a platform API React Native does not already give you. React Native's current guide says your application code may need to interact with native platform APIs that are not provided by React Native or an existing library, and that you can write the integration code yourself using a Turbo Native Module. It describes a Turbo Native Module as three things at once: your specification, the native code you write, and the interfaces Codegen generates from that specification. The specification is TypeScript or Flow. The code you write is Java or Kotlin on Android and an Objective-C++ file on iOS. Read 17 October 2026.
Describe the app, then find out if any of this applies
Most apps never reach the line where someone has to write Swift. Build the thing first, in a project you own, and let the one capability that needs a module declare itself instead of planning for it on day one.
Start building