Expo vs Bare React Native Comparison

A managed React Native framework set up with Node.js LTS, reversible thanks to CNG

VS
Bare React Native

The core React Native setup, giving full control over the native toolchain

16 min readCross-Platform

Quick Verdict

In 2026 the default answer is Expo + CNG: since `expo prebuild` regenerates the native folders, "eject" is no longer a one-way door, the config plugin system covers most native configuration, and EAS Build's free tier is enough for a small-to-mid-sized project. The reasons that justify Bare React Native are narrow but real: a custom toolchain EAS doesn't support, a mandatory on-prem CI requirement, or a very rare native SDK. If none of those apply, start with Expo.

ExpoBare React Native
Read the full verdict

Score Comparison

Loading chart...

Detailed Scoring

Detailed Scoring: Expo and Bare React Native — category-by-category scores out of 10
CategoryExpoBare React Native
Performance
8/10
8/10
Ease of Learning
9/10
6/10
Ecosystem
8/10
8/10
Community
8/10
8/10
Job Market
7/10
8/10
Future-Proof
8/10
7/10

Pros & Cons

Expo

Pros

  • Setup requires only Node.js LTS — test instantly with Expo Go/Snack without Xcode/Android Studio
  • CNG (`expo prebuild`) regenerates native folders — 'eject' is no longer a one-way door
  • The config plugin system configures the native project programmatically and reproducibly
  • EAS Update ships instant OTA JS updates separate from the native layer
  • The EAS Build Free plan includes 15+15 builds/month and 1,000 MAU updates — a no-cost start for a small project
  • EAS Workflows trigger automated native builds from GitHub events/cron, driven by the app config
  • Upgrading is as simple as updating an npm dependency plus `prebuild --clean`
  • As a founding member of the React Foundation, Expo stays closely aligned with the RN ecosystem

Cons

  • Expo SDKs trail the RN core by one version (SDK 57 → RN 0.86, not RN 0.87) — you can't get the newest RN feature immediately
  • Expo Go (iOS) has required login in the terminal and the app since September 3, 2026
  • Writing a config plugin for very rare/specialized native SDKs can take extra effort
  • Once the EAS Free plan is exceeded, you must move to a paid tier: Starter ($19/month) or Production ($199/month)
  • Because EAS is designed cloud-first, it fits poorly with a mandatory on-prem enterprise CI requirement
  • Hand-editing native folders and then running prebuild silently wipes those changes (unless moved into a config plugin)

Best For

Teams wanting to start a new React Native project quicklySmall-to-mid-sized projects that can use EAS's cloud CI/CDMVP/prototype development and fast iterationApps with heavy OTA update needs that want to ship JS fixes without waiting for store reviewProjects whose native needs are covered by the standard config plugin ecosystem (camera, notifications, location, etc.)

Bare React Native

Pros

  • Direct, unlimited access to the native project (Xcode/Gradle) — no abstraction layer
  • You get every RN minor release (0.85, 0.86, 0.87...) immediately, without waiting for the Expo SDK cycle
  • No dependency on EAS at all — set up any CI/CD service or on-prem pipeline you want
  • You can integrate any native SDK directly, not limited to the config plugin ecosystem
  • 126,507 GitHub stars and 25,238 forks back a large, mature community
  • MIT-licensed, open source, no vendor lock-in

Cons

  • Setup requires Xcode + CocoaPods, Android Studio + SDK + Gradle — a noticeably longer initial setup than Expo
  • The native `ios`/`android` folders are permanent; every version upgrade requires manually applying diffs via Upgrade Helper
  • There's no ready-made OTA update solution — you must manually integrate `expo-updates` or set up an alternative
  • Even if you want to use CI/CD (including EAS), you must manually sync native build credentials
  • Extra native package configuration (Info.plist permissions, Manifest entries, Gradle settings) is done by hand — there's no config plugin reproducibility
  • Toolchain setup for a new developer joining the team noticeably extends the time to the first build

Best For

Projects requiring a custom toolchain or build environment that EAS doesn't supportTeams with a mandatory enterprise on-prem/self-hosted CI requirementApps requiring a very rare or highly specialized native SDK integration that can't be covered by a config pluginLarge, complex apps that need continuous, deep intervention in native codeTeams that want to use the newest RN core version without waiting for the SDK cycle

Code Comparison

Expo
// Expo — adding a native permission via app.config.ts + a local config plugin (TypeScript)

// plugins/withCameraUsage.ts — config plugin that adds camera permission to Info.plist
import { ConfigPlugin, withInfoPlist } from "expo/config-plugins";

const withCameraUsage: ConfigPlugin = (config) =>
  withInfoPlist(config, (config) => {
    config.modResults.NSCameraUsageDescription =
      "Profil fotoğrafı çekmek için kameraya erişim gerekiyor";
    return config;
  });

export default withCameraUsage;

// app.config.ts — the plugin is referenced by file PATH (requires npm i -D tsx)
import "tsx/cjs";
import { ExpoConfig, ConfigContext } from "expo/config";

export default ({ config }: ConfigContext): ExpoConfig => ({
  ...config,
  name: "MyApp",
  slug: "my-app",
  version: "1.0.0",
  plugins: [["./plugins/withCameraUsage.ts"], "expo-router", "expo-updates"],
  updates: { url: "https://u.expo.dev/your-project-id" },
  runtimeVersion: { policy: "appVersion" },
});

// Terminal: generate the native folders (CNG) and apply them
// npx expo prebuild --clean

// Run EAS Build and publish an OTA update
// eas build --platform all --profile production
// eas update --branch production --message "Bug fix: login crash"
Bare React Native
// Bare React Native — package installation + manual native configuration (TypeScript)

// 1) Install the package: autolinking links it, fetch pods, rebuild
// $ npm install react-native-vision-camera react-native-nitro-modules react-native-nitro-image
// $ npx pod-install
// $ npm run ios     # Android: npm run android

// 2) Add permissions MANUALLY — this step is not part of autolinking
// ios/MyApp/Info.plist:
// <key>NSCameraUsageDescription</key>
// <string>We need camera access to take a profile photo</string>
// android/app/src/main/AndroidManifest.xml:
// <uses-permission android:name="android.permission.CAMERA" />

// 3) Usage on the JS side (shared code)
import React, { useEffect } from "react";
import { Camera, useCameraPermission } from "react-native-vision-camera";

function ProfileCamera() {
  const { hasPermission, requestPermission } = useCameraPermission();

  useEffect(() => {
    if (!hasPermission) requestPermission();
  }, [hasPermission, requestPermission]);

  return <Camera style={{ flex: 1 }} isActive={true} device="back" />;
}

export default ProfileCamera;

// 4) Upgrading (0.86 -> 0.87): there is NO CLI command for this
// Upgrade Helper (web): https://react-native-community.github.io/upgrade-helper/
// -> apply the diff manually to Podfile.lock, build.gradle, AppDelegate.swift

// 5) Your own CI pipeline (GitHub Actions, summary)
// - run: npx pod-install
// - run: xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp -configuration Release

Conclusion

In 2026 the default answer is Expo + CNG: since `expo prebuild` regenerates the native folders, "eject" is no longer a one-way door, the config plugin system covers most native configuration, and EAS Build's free tier is enough for a small-to-mid-sized project. The reasons that justify Bare React Native are narrow but real: a custom toolchain EAS doesn't support, a mandatory on-prem CI requirement, or a very rare native SDK. If none of those apply, start with Expo.

Get Free Consultation
FAQ

Frequently Asked Questions

In 2026 the default answer is Expo + CNG: the config plugin system covers most native modules, EAS Build's free tier (15+15 builds/month) is enough for a small-to-mid-sized project, and thanks to `expo prebuild`, dropping down to native code is no longer an irreversible decision. The real reasons that justify bare RN: a custom toolchain EAS doesn't support, a mandatory enterprise on-prem CI requirement, or a very rare native SDK that a config plugin can't cover.

Related Blog Posts

View All Posts

Related Projects

View All Projects
All Comparisons