Expo has evolved from “managed only” to a full toolkit that can still eject to bare when needed. Here’s how I think about Expo vs bare React Native in 2025.
Expo (EAS, managed or custom dev client)
Pros:
- EAS Build — Build in the cloud without Xcode/Android Studio on your machine. Signing and provisioning are handled. Great for CI and for devs who don’t want to maintain native toolchains.
- EAS Submit — Push builds to TestFlight and Play Internal (or production) from the CLI. Integrates with EAS Build so you can go from commit to store in one pipeline.
- Expo SDK — Camera, maps, notifications, updates, and more via a consistent API. Often less native code to write and upgrade.
- Over-the-air updates — EAS Update lets you ship JS (and asset) changes without a full store release. Useful for hotfixes and A/B tests.
-
Documentation and DX — Docs are good, and the CLI (e.g.
npx expo start) is smooth. Fast Refresh and dev client make iteration fast. - Custom dev client — You can add native modules and still use EAS Build and most of the Expo tooling. You’re not locked out of native code.
Cons:
- Native control — For very custom native behavior or bleeding-edge native APIs, you might need to patch or fork. Usually it’s manageable with config plugins or a custom dev client.
- Bundle and runtime — Expo adds some size and abstraction. For most apps it’s fine; for very constrained environments you might care.
- Learning curve — If the team has only ever used bare RN, they’ll need to learn EAS and the Expo way of doing things.
When I choose it: For most new React Native apps. EAS Build and Submit alone save a lot of pain. I use a custom dev client when I need native modules that aren’t in the default SDK. I use managed when the app stays within Expo’s supported surface.
Expo prebuild: how native projects are generated
prebuild is the step that creates (or updates) the ios/ and android/ folders from your Expo config. You don’t have to write those projects by hand—Expo generates them from app.json / app.config.js and config plugins.
-
What it does — Reads your app config and plugin list, then generates a native Xcode and Android project. Config plugins run during prebuild to add permissions, native dependencies, or custom native code (e.g.
expo-camera,expo-notifications). So you stay in a config-driven workflow even when you need native code. -
When it runs — In EAS Build, prebuild runs by default before each build unless you’ve committed
ios/andandroid/and use a profile that skips it. Locally, you runnpx expo prebuildwhen you want to generate or refresh the native projects. Usenpx expo prebuild --cleanto regenerate from scratch (useful after changing plugins or app config). -
Managed vs bare-ish — In a managed workflow you often don’t commit
ios/andandroid/; EAS Build runs prebuild every time so the native project always matches your config. In a custom dev client or “bare-ish” workflow you might run prebuild once, then commit the native folders and edit them. After that, either keep maintaining those folders or run prebuild again and re-apply your changes (or use config plugins so the changes are reproducible). - Why it matters — prebuild is what lets you add native modules and still use EAS Build and Expo tooling. You’re not stuck choosing between “no native code” and “fully manual native projects.” You get generated native projects that stay in sync with your Expo config and plugins.
Bare React Native
Pros:
- Full control — Every native file is yours. You can change anything, add any library, and tune the native project exactly how you want.
- No Expo layer — Slightly smaller surface area: no Expo runtime or config plugins. Some teams prefer “just RN + native.”
- Existing projects — Plenty of codebases started bare. If you’re maintaining one, staying bare can be simpler than migrating to Expo.
Cons:
- Build and signing — You own Xcode/Android Studio, certificates, provisioning, and CI. That’s a real cost in time and frustration.
- Updates — You have to upgrade React Native and native deps yourself. Expo abstracts some of that; bare doesn’t.
- No EAS Update — You can add a different OTA solution (e.g. CodePush), but it’s not built in like EAS Update.
When I choose it: When the app needs native behavior Expo doesn’t support (or would require heavy patching), when the team already has a bare setup they’re happy with, or when we need to match an existing bare codebase. I don’t go bare “just because”—I go bare when there’s a clear reason.
Summary
- New app, standard needs — I start with Expo (managed or custom dev client) and use EAS Build + Submit. I only go bare if we hit a wall.
- Heavy native or existing bare — I use or stay with bare when control or existing investment justifies it.
- 2026 — Expo is production-ready and widely used. The “Expo = can’t use native” idea is outdated. Choose based on your app’s needs and your team’s comfort with native tooling, not on old stereotypes.
Saad Mehmood — Portfolio
Top comments (0)