Skip to main content
Hire React Native Developers

React Native · Blog

Expo vs Bare React Native in 2026: Which Should Your Developer Know?

The debate is over, but the hiring implications aren't.

May 27, 202623 min readIntermediate
  • React Native
  • Expo
  • Bare Workflow
  • EAS
  • Mobile Development
Back to all articles
On this page

The debate is over, but the hiring implications aren't.

The React Native core team no longer recommends starting new projects with react-native init. Expo is the recommended default for new projects in 2026. Approximately 83% of projects built with EAS Build use the New Architecture, and 70% of new React Native projects start with Expo's managed workflow. The old "Expo is for prototypes, bare is for production" advice is dangerously out of date.

But here's the problem with hiring: Expo-managed workflow and bare React Native still produce fundamentally different developer skill sets. A developer who has built exclusively in Expo has never configured a Podfile, never opened Xcode's build settings, never debugged a CocoaPods conflict, and never written a native module in Swift or Kotlin. Drop that developer into a bare workflow codebase with custom native bridges, and they'll be underwater within the first week.

This guide explains what each workflow involves in 2026, when each is the right choice for your project, and how the decision affects whom you should hire.

What Expo and Bare React Native Actually Mean in 2026

The terminology has shifted. In 2026, there are three workflows, not two.

Expo Managed Workflow. You never touch native code. Expo handles builds, signing, native module configuration, and over-the-air updates. Your developer writes TypeScript and uses Expo SDK APIs. When they need a native capability (camera, push notifications, maps, biometrics), they install an Expo SDK package or use a config plugin. They never open Xcode or Android Studio.

Expo Bare Workflow. You get Expo's tooling (EAS Build, EAS Update, Expo Router, Expo SDK) and full access to the native iOS and Android project files. Your developer can write custom native modules, modify Podfiles, and configure Gradle directly. This is the middle ground that didn't exist in 2020.

Bare React Native (no Expo). You manage everything yourself. Native builds happen locally in Xcode and Android Studio. OTA updates require a third-party solution (CodePush, which Microsoft has deprecated, or a custom system). Upgrades require manual changes to native configuration files. This is increasingly rare for new projects but still common in legacy codebases.

The practical difference for your developer:

# Expo managed: Build and submit to stores from the command line
# No Xcode, no Android Studio, no local certificates
eas build --platform all --profile production
eas submit --platform ios
eas submit --platform android
# Time: 30-60 minutes for first production build setup

# Bare React Native: Build locally
# Requires Xcode installed, certificates configured, provisioning profiles managed
open ios/MyApp.xcworkspace  # configure signing, archive manually
cd android && ./gradlew bundleRelease  # configure keystore, build manually
# Time: 2-4 hours for first production build (certificates, signing, environment)

The 30-60 minute vs 2-4 hour difference in the first build repeats at smaller increments each day of development. Over a 6-month project, Expo's managed workflow saves 15-20% of total development time.

The Complete Feature Comparison

Before diving into benchmarks and code, here's the comprehensive side-by-side that covers every dimension hiring managers and CTOs ask about:

FactorExpo ManagedExpo BareBare React Native (no Expo)
Setup timeMinutes (single CLI command)30-60 min (Expo + native project access)Hours (Xcode, Android Studio, CocoaPods, Gradle)
Native code accessThrough Expo Modules API and config pluginsFull access to ios/ and android/ directoriesDirect access to all native project files
Build processCloud builds via EAS (no Xcode/Android Studio needed)Cloud or local buildsLocal builds requiring native tooling on every dev machine
OTA updatesBuilt-in through EAS UpdateBuilt-in through EAS UpdateRequires third-party (CodePush deprecated) or custom solution
NavigationExpo Router (file-based, deep linking automatic)Expo Router or React NavigationChoose and configure manually
Native module devSwift/Kotlin through Expo Modules APISwift/Kotlin directly or via Expo ModulesSwift/Kotlin/Java with manual linking
Upgrade pathCNG regenerates native project from configSemi-managed through Expo CLIManual native file changes per version
Runtime performanceIdentical (same New Architecture)IdenticalIdentical
App size (minimal)Under 3 MB on App StoreUnder 3 MBUnder 2 MB
Learning curveLow (JavaScript/TypeScript only)Medium (JS + some native awareness)High (JS + native tooling expertise)
Mac required for iOSNo (EAS cloud builds)Optional (EAS or local)Yes (Xcode is macOS only)
Device testing during devExpo Go app (scan QR, instant preview)Custom dev client (build once, then instant)Full native build for every test cycle

Developer Salaries by Workflow

The workflow your project uses directly affects what you pay. These ranges reflect US-based positions from Glassdoor data for 2026:

Experience LevelExpo-Focused DeveloperBare Workflow DeveloperBare + Native Modules (Swift/Kotlin)
Junior (0-2 years)$72,000-$92,000$80,000-$100,000Rare at this level
Mid-level (3-5 years)$95,000-$128,000$110,000-$140,000$120,000-$150,000
Senior (5+ years)$120,000-$155,000$135,000-$170,000$145,000-$185,000
Bay Area / NYC premium+$15,000-$25,000+$15,000-$25,000+$20,000-$30,000

The salary gap between Expo-focused and bare-workflow developers reflects the additional skills required: knowledge of native tooling, platform debugging, certificate management, and build configuration. The premium for native module development (Swift/Kotlin) is driven by the rarity of developers who bridge both JavaScript and native disciplines.

Development Cost: The Same App Built Three Ways

For a mid-complexity app (auth, data feeds, camera, push notifications, maps, offline storage) with a team of two developers over six months:

Cost CategoryExpo ManagedBare WorkflowDifference
Environment setup1 day3 days+2 days
Feature development~100 days~105 days+5 days
Native configuration and debugging~5 days~15 days+10 days
CI/CD pipeline setup2 days5 days+3 days
App store submission3 days3 daysSame
Total development time~111 days~131 days+20 days

At $150/hour for a senior developer, those 20 extra days cost $24,000-$36,000 in developer time alone.

Monthly infrastructure costs also differ:

InfrastructureExpoBare React Native
Cloud builds$29/month (EAS Production plan, unlimited)$100-$300/month (GitHub Actions or Bitrise)
OTA updatesIncluded in EAS$40/month (if using CodePush) or custom
Push notification serviceIncluded in Expo SDK$20-$50/month (OneSignal, FCM setup)
Build machinesNot needed (cloud builds)$0 local or $200+/month CI
Monthly total~$29~$160-$590

Over 12 months, the infrastructure cost difference is $1,600-$6,700 in Expo's favor. Combined with the development time savings, Expo's total cost advantage on a standard app is $30,000-$45,000 over a six-month project.

The Benchmarks That Actually Matter

Most "Expo vs bare" comparisons focus on feature lists. The numbers that actually affect your project are performance, app size, and build time.

MetricExpo ManagedBare React NativeWhat It Means
Runtime performanceIdenticalIdenticalBoth use the same New Architecture (Fabric, JSI, Turbo Modules). Benchmarks show a difference of less than 5% in 2026. Expo's overhead is in dev tools, not runtime
Cold startup~100ms slower on very old devicesBaselineNegligible on modern devices. Not user-perceptible on any phone sold after 2021
App size (minimal app)Under 3 MB on App StoreUnder 2 MB5-10% larger (2-4 MB difference). Irrelevant for most apps. Matters only for ultra-low-end devices in emerging markets
Build time (CI)8-15 min (EAS cloud)15-30 min (local or CI)EAS cloud builds are faster because Apple Silicon runners handle iOS builds without your machine
Upgrade effortManaged by Expo CLIManual native config changesReact Native version upgrades have historically been one of the most painful parts of the framework. Expo abstracts most of this
OTA updatesBuilt-in (EAS Update)Requires third-party (CodePush deprecated)OTA lets you push JS fixes without App Store review. Critical for fast bug response

The key takeaway is that runtime performance is identical. The "Expo is slower" myth dates back to 2018, when it was true. In 2026, both workflows execute on the same Hermes engine, the same JSI bindings, the same Fabric renderer. The difference is developer experience, not app performance.

What production-scale Expo actually looks like

Benchmarks on paper are one thing. What matters is whether Expo holds up when 100,000+ people use your app every month.

Published production retrospectives from teams running Expo with the New Architecture consistently report numbers that match or exceed those of native apps. One cross-platform team shared their data after six months of production use with a six-figure user base:

What They MeasuredResultWhat It Means for Hiring
App stabilityOver 99% crash-free sessions across both platformsComparable to native iOS/Android apps. The "Expo isn't stable enough for production" argument doesn't hold
Development velocityRoughly 14 hours per feature on averageNearly 40% faster per feature than the same team's previous Flutter implementation. Team familiarity with React was the main driver
Communication overheadOver 40% reduction in JS-to-native call latencyBridgeless architecture (New Architecture) eliminates the serialization bottleneck that caused stuttering on older RN versions
CI/CD pipelineUnder 10 minutes for a production Android buildEAS Build on cloud runners. No local machine dependency
Critical bug responseUnder 15 minutes from fix to all users receiving itEAS Update OTA. No App Store review. This is the capability bare React Native cannot match without custom infrastructure

The velocity number matters most for hiring decisions. Feature development speed correlates directly with the amount of native configuration overhead your developer carries. In Expo managed workflow, that overhead approaches zero. In bare React Native, it's a constant tax on every task that touches the build pipeline, certificates, or native dependencies.

Where the code difference shows up in practice

The real tradeoff between Expo and bare isn't theoretical. It shows up in the code your developer writes for everyday features. Here's a practical example: securely storing a user's session token to keep them logged in across app restarts.

// EXPO MANAGED: Secure token storage
// Total setup: install one package, write four lines
import * as SecureStore from 'expo-secure-store';

// Save token (encrypted by iOS Keychain / Android Keystore automatically)
await SecureStore.setItemAsync('session_token', token);

// Retrieve token on app launch
const savedToken = await SecureStore.getItemAsync('session_token');

// Delete on logout
await SecureStore.deleteItemAsync('session_token');
// Done. No Podfile edit. No Gradle change. No native code.

// BARE REACT NATIVE: Same feature, more moving parts
// Setup: install package, run pod install, configure Android, write adapter
import * as Keychain from 'react-native-keychain';

// Save token with platform-specific encryption
await Keychain.setGenericPassword('session', token, {
  service: 'com.myapp.auth',
  accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
});

// Retrieve on app launch
const credentials = await Keychain.getGenericPassword({
  service: 'com.myapp.auth',
});
const savedToken = credentials ? credentials.password : null;

// Delete on logout
await Keychain.resetGenericPassword({ service: 'com.myapp.auth' });

// But first, you also needed to:
// 1. npm install react-native-keychain
// 2. cd ios && pod install (and fix any CocoaPods version conflict)
// 3. Verify Android Keystore configuration in build.gradle
// 4. Handle the edge case where Keychain returns false instead of null on some Android versions
// 5. Test across 3+ Android manufacturers (Samsung, Xiaomi, and Pixel all behave differently)

Both approaches store the token securely. Both encrypt using the OS keychain. The Expo version takes 5 minutes to implement. The bare version takes 30-60 minutes, including the native setup steps, and introduces platform-specific edge cases your developer needs to test for. Multiply that difference across every native feature in your app (push notifications, camera, biometrics, file system, haptics), and the cumulative time savings over a 6-month project are substantial.

Another real example: push notifications

Push notifications are in almost every production app. Here's what the setup looks like in each workflow:

// EXPO: Push notification setup
// Total time: ~20 minutes including testing
import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';

async function registerForPushNotifications() {
  // Expo handles permission requests on both platforms
  const { status } = await Notifications.requestPermissionsAsync();
  if (status !== 'granted') return null;

  // Get the push token (works on both iOS and Android)
  const token = await Notifications.getExpoPushTokenAsync({
    projectId: 'your-project-id',
  });
  return token.data;
}

// Listen for incoming notifications
Notifications.addNotificationReceivedListener((notification) => {
  const { title, body } = notification.request.content;
  // Handle the notification
});

// That's it. No native code. No Info.plist edits. No Gradle changes.
// Expo's config plugin handles APNs certificates and FCM setup.

# BARE: Push notification setup
# Total time: 2-4 hours including debugging

# 1. Install the package
npm install @react-native-firebase/app @react-native-firebase/messaging

# 2. iOS: Add GoogleService-Info.plist to ios/ directory via Xcode
#    (drag and drop into Xcode, select "Copy items if needed")

# 3. iOS: Edit ios/Podfile - add Firebase pods
#    pod 'Firebase/Messaging'

# 4. iOS: Run pod install (and fix version conflicts if they appear)
cd ios && pod install && cd ..

# 5. iOS: Edit AppDelegate.mm - add Firebase import and configure call
#    #import <Firebase.h>
#    [FIRApp configure];

# 6. iOS: Enable Push Notifications capability in Xcode
#    Targets → Signing & Capabilities → + Capability → Push Notifications

# 7. iOS: Upload APNs key to Firebase Console

# 8. Android: Add google-services.json to android/app/
# 9. Android: Edit android/build.gradle - add Google services classpath
# 10. Android: Edit android/app/build.gradle - apply Google services plugin
# 11. Android: Create a notification channel for Android 13+

# Now you can write the JavaScript code (similar to Expo)
# But any mistake in steps 2-11 produces a silent failure
# where notifications simply don't arrive, with no error message.

The 11-step bare setup isn't difficult for an experienced native developer. But each step is a potential silent failure point. If you miss step 6 (the Xcode capability toggle), notifications simply don't arrive on iOS, and there's no error message. If your google-services.json has a typo in the package name, Android notifications fail silently. Debugging these issues requires platform-specific knowledge that many React Native developers lack.

An Expo developer handles push notifications in 20 minutes because the config plugin manages all 11 of those steps automatically. A bare developer handles them in 2-4 hours and needs to know which Xcode panel to click and which Gradle file to edit. Both produce working push notifications. The question is whether those extra hours multiply across your entire feature set.

The same pattern repeats for camera access

// EXPO: Camera with permissions in one file
import { CameraView, useCameraPermissions } from 'expo-camera';
import { useState } from 'react';
import { View, Text, Pressable, StyleSheet } from 'react-native';

export default function CameraScreen() {
  const [permission, requestPermission] = useCameraPermissions();
  const [facing, setFacing] = useState<'front' | 'back'>('back');

  if (!permission?.granted) {
    return (
      <View style={styles.container}>
        <Text>Camera access is needed to take photos</Text>
        <Pressable style={styles.button} onPress={requestPermission}>
          <Text style={styles.buttonText}>Allow Camera</Text>
        </Pressable>
      </View>
    );
  }

  return (
    <CameraView style={styles.camera} facing={facing}>
      <Pressable
        style={styles.flipButton}
        onPress={() => setFacing(f => f === 'back' ? 'front' : 'back')}
      >
        <Text style={styles.buttonText}>Flip</Text>
      </Pressable>
    </CameraView>
  );
}

// Install: npx expo install expo-camera
// Permissions: handled by config plugin automatically
// No Info.plist edit. No AndroidManifest change. No pod install.

# BARE: Same camera screen requires native configuration

# 1. Install the package
npm install react-native-vision-camera

# 2. iOS: Edit ios/MyApp/Info.plist (add camera usage description)
#    <key>NSCameraUsageDescription</key>
#    <string>This app needs camera access to take photos</string>

# 3. iOS: Run pod install
cd ios && pod install && cd ..

# 4. Android: Edit android/app/src/main/AndroidManifest.xml
#    <uses-permission android:name="android.permission.CAMERA" />

# 5. Android: Verify minSdkVersion >= 21 in android/app/build.gradle

# 6. Import and use (API is different from Expo's camera)
#    import { Camera, useCameraDevice } from 'react-native-vision-camera';
#    const device = useCameraDevice('back');
#    <Camera device={device} isActive={true} style={StyleSheet.absoluteFill} />

# 7. Handle the common gotcha: camera preview is black on Android
#    emulator because emulators don't have real cameras.
#    Must test on physical device.

The pattern is identical to push notifications: Expo handles the native configuration through config plugins, bare requires manual edits to platform-specific files. For a single feature, the difference is 20 minutes vs 1-2 hours. Across an app with camera, push notifications, location, biometrics, file system access, and haptics, the cumulative difference is measured in weeks.

What Expo Gives You That Bare Doesn't

These are the capabilities that make Expo the default choice for new projects. Each one represents work that your developer either gets for free (Expo) or has to build and maintain themselves (bare).

EAS Build. Cloud-based builds that handle code signing, provisioning profiles, and native dependencies. Your developer doesn't need a Mac to build for iOS. They don't manage certificates locally. The build runs on Expo's servers and produces a signed binary ready for the store.

EAS Update. Over-the-air updates that push JavaScript and asset changes directly to users without going through the App Store review process. When your developer fixes a critical bug on Friday afternoon, users get the fix within minutes, not after a 1-3-day review cycle. Here's what deploying an OTA fix looks like:

# Fix the bug in your TypeScript code
# Then push the update to all production users
eas update --branch production --message "Fix checkout crash on Android 14"

# Users get the fix on next app launch. No store review.
# Rollback if something goes wrong:
eas update:rollback --branch production

This capability alone justifies Expo for most teams. The ability to ship a critical fix in 10 minutes instead of 3 days changes how you think about risk.

Expo Router. File-based navigation that maps directly to your screen structure. Deep linking works by default without days of configuration. If you've used Next.js, 80% of the mental model transfers.

// File structure IS your navigation structure
app/
  (tabs)/
    index.tsx        // → /
    profile.tsx      // → /profile
    settings.tsx     // → /settings
  product/
    [id].tsx         // → /product/123 (deep link works automatically)
  _layout.tsx        // Tab bar layout

// Deep link: myapp://product/456 → opens product screen with id=456
// No manual linking config. No URL scheme setup. It just works.

Expo SDK. Over 70 APIs for camera, notifications, haptics, filesystem, sensors, biometrics, and more. Each is a cross-platform abstraction that handles iOS and Android differences internally. Your developer writes one API call. It works on both platforms.

Config Plugins. This is what killed the old "Expo can't do native" argument. Config plugins let you modify native iOS and Android project files from JavaScript, without ejecting and without manually editing Xcode or Gradle. Need to add a custom URL scheme, modify Info.plist, or add an Android permission? Write a config plugin. The native project is regenerated from your config on every build.

// app.json - adding a custom URL scheme without touching Xcode
{
  "expo": {
    "scheme": "myapp",
    "plugins": [
      ["expo-camera", { "cameraPermission": "Allow $(PRODUCT_NAME) to access your camera" }],
      ["expo-notifications", { "icon": "./assets/notification-icon.png" }]
    ],
    "ios": {
      "infoPlist": {
        "UIBackgroundModes": ["remote-notification", "fetch"]
      }
    }
  }
}
// No Xcode required. The native project is generated from this config.

Continuous Native Generation (CNG). This is the technical concept underlying everything above, and it's what makes Expo's managed workflow fundamentally different from bare React Native, not just "easier bare React Native."

In bare React Native, the ios/ and android/ directories are source code. Your developer edits them, commits them to Git, and maintains them across every React Native version upgrade. When React Native ships a new version, your developer has to reconcile the changes with their custom modifications manually. This process has historically been the most frustrating part of working with React Native, sometimes consuming entire days of debugging CocoaPods version mismatches and Gradle configuration conflicts.

In Expo's managed workflow, the native directories don't exist in your source code. They're generated fresh on every build from your app.json configuration and config plugins. The native project is a build artifact, not something your developer maintains.

The practical impact on upgrades:

# Bare React Native: upgrading from 0.74 to 0.76
# Read the upgrade helper diff for every file that changed
# Manually patch ios/Podfile, android/build.gradle, android/app/build.gradle
# Resolve CocoaPods conflicts (expect 2-3 per major upgrade)
# Fix native import paths that changed between versions
# Rebuild, test, fix what broke
# Budget: half a day to two full days depending on version gap

# Expo: upgrading SDK version
npx expo install expo@latest --fix
# CNG regenerates the native project with correct configuration
# Your config plugins re-apply your customizations automatically
# Budget: 20-40 minutes including smoke testing on both platforms

This is why teams that maintain bare React Native codebases often describe upgrades as their least favorite part of the job. It's also why developers who have only worked with Expo may underestimate the difficulty of maintaining a bare project. Both experiences are real. Understanding which one your project involves is essential before you hire.

When Bare React Native Is Still the Right Choice

Expo is the default, but it's not universal. There are specific situations where bare React Native (with or without Expo's tooling layer) is the right call.

Your app needs custom native modules that Expo doesn't support. If you're integrating a proprietary SDK (a specific payment processor's native SDK, a custom BLE protocol for medical devices, a hardware manufacturer's API), you need direct access to native project files. Expo's config plugins handle many cases, but some native integrations require modifications that go beyond what plugins can express.

You have an existing bare React Native codebase. Migrating a large bare codebase to Expo managed workflow is possible but non-trivial. If the team is productive and the codebase is stable, the migration cost may not be justified. Adopt Expo's tooling (EAS Build, EAS Update) incrementally without converting to managed workflow.

Your app does heavy C++ or native GPU work. Real-time video processing, custom ML inference pipelines, or game engine integrations that require direct access to native build configurations and linking against C++ libraries.

Your team has dedicated iOS and Android engineers. If your team already has native mobile expertise and is comfortable managing Xcode, Gradle, CocoaPods, and platform-specific build pipelines, bare React Native lets them work in the environment they know.

How This Changes Who You Hire

This is the section that determines your budget and your sourcing strategy.

Your ProjectDeveloper ProfileWhat They Need to KnowSalary Range (US)
New app, standard features (most apps)Expo-focused developerTypeScript, Expo Router, Expo SDK, EAS Build/Update, React Navigation, state management$85,000-$128,000
New app, custom hardware integrationsBare workflow + native skillsEverything above + Swift/Kotlin, Xcode/Android Studio, Podfile/Gradle config, Turbo Modules$130,000-$185,000
Existing bare codebase, maintenance + featuresBare workflow developerComfortable with native project files, manual builds, native debugging. Expo knowledge is a bonus$100,000-$145,000
Existing bare codebase, migrating to New ArchitectureNew Architecture specialistJSI, Fabric, Turbo Module Codegen specs, Hermes profiling, native crash debugging$145,000-$185,000
Startup MVP, need to ship in 8 weeksExpo-first generalistExpo managed workflow, Firebase/Supabase, fast iteration. Native skills not needed$85,000-$110,000

The most expensive hiring mistake: paying $145,000+ for a bare-workflow specialist when your app runs entirely in Expo managed workflow. The inverse is equally costly: hiring an Expo-only developer at $90,000 for a project that requires custom Turbo Module development, only to discover three months in that they've never opened Xcode.

Put your workflow in the job description. This single line filters out the wrong candidates before the phone screen. "Expo managed workflow" or "Bare React Native with custom native modules" are two different jobs that happen to share the same framework name.

What to Ask in Interviews

These questions separate Expo-only developers from bare-capable developers. Use them when the distinction matters for your project.

"Have you ever configured a Podfile or resolved a CocoaPods conflict?" An Expo-only developer has never needed to. A bare developer has war stories. This single question tells you which side of the line they're on.

"How do you handle a native capability that doesn't have an Expo SDK package?" Strong answer: "I check if there's a config plugin. If not, I create a custom dev client with expo-dev-client and write or integrate the native module. If the integration is complex enough, I drop to bare workflow for that module." Weak answer: "I'd look for an npm package."

"Walk me through how you'd push a critical bug fix to production users on a Friday evening." Expo developers describe EAS Update: push JS changes; users get the fix on the next launch; rollback if needed. Bare developers describe a full release cycle: build locally, submit to TestFlight/Play Console, wait for review. Both are valid answers. The question reveals their experience with deployment workflows.

"How does the upgrade process differ between Expo managed and bare React Native?" A strong answer explains that Expo manages native dependency upgrades through the SDK version, whereas bare requires manual changes to Podfile, build.gradle, and native project configs. They should mention that bare upgrades have historically been the most painful part of React Native development, and Expo's Continuous Native Generation (CNG) model regenerates the native project from app.json on every build, largely solving this problem.

For more interview questions across all skill areas, see our interview questions guide.

Before You Choose Bare: Test Your Blocker First

The most common reason teams choose bare React Native is "we need a native library that Expo doesn't support." In 2026, that assumption is wrong more often than it's right. Expo's custom development client lets you use any native npm package without leaving the managed workflow. Before you commit to bare (and the higher developer salary and longer setup time that comes with it), test whether your suspected blocker actually blocks.

# Step 1: Create a minimal test project
npx create-expo-app blocker-test
cd blocker-test

# Step 2: Add the custom dev client (this enables any native code)
npx expo install expo-dev-client

# Step 3: Install the native library you think requires bare RN
npm install your-suspected-blocker-library

# Step 4: Build a custom dev client (runs once, takes ~10 minutes)
eas build --profile development --platform ios

# Step 5: Install the dev build on your device or simulator
# Then start the app:
npx expo start --dev-client

# Step 6: Import and use the library in a test screen
# If it works -> you don't need bare React Native
# If it crashes -> check the error. Most failures are fixable
#   with a config plugin or a minor native patch
# If it fundamentally can't work -> bare is justified

This test takes under an hour and can save you months of unnecessary complexity. We've seen teams commit to bare React Native for a library that works perfectly in Expo's dev client, only to spend weeks dealing with native build issues they didn't need to.

Expo vs Bare: The Production Pipeline

The workflow difference isn't just about initial setup. It shows up every day in how your team builds, tests, and deploys.

# EXPO: Full production deployment pipeline
# Developer fixes a bug at 4 PM on Friday

# 1. Fix the code (TypeScript only)
git commit -m "fix: resolve cart total rounding error"
git push origin main

# 2. CI runs tests automatically (configured once in eas.json)
# Jest + React Native Testing Library: 3 minutes

# 3a. If it's a JS-only fix (most fixes): push OTA update
eas update --branch production --message "Fix cart rounding"
# Users get the fix on next app launch. Total time: ~15 minutes.

# 3b. If it requires a native change: trigger a full build
eas build --platform all --profile production
# Cloud build: ~12 minutes. Then submit:
eas submit --platform ios
eas submit --platform android
# Total time: ~30 minutes + store review (1-3 days)

# BARE: Same bug fix on a Friday afternoon

# 1. Fix the code
git commit -m "fix: resolve cart total rounding error"
git push origin main

# 2. CI runs tests (you configured GitHub Actions / Bitrise yourself)
# Jest: 3 minutes. Build verification: 15-20 minutes.

# 3. Build locally or via CI
# iOS: Open Xcode, increment build number, archive, upload to App Store Connect
# Android: Run ./gradlew bundleRelease, sign with keystore, upload to Play Console
# Total build time: 20-30 minutes

# 4. Submit for review
# No OTA option. Even a one-line JS fix requires full store review.
# Total time: 30-45 minutes + store review (1-3 days)

# The difference: Expo can ship JS fixes in 15 minutes.
# Bare RN ships the same fix in 1-3 days.

For a team that ships weekly updates, this pipeline difference compounds into weeks of saved time per year. For a team that ships hotfixes for critical bugs, the ability to bypass store review is the difference between a 15-minute response and a 3-day response.

The Myths That Still Circulate

"Expo is only for small apps." This was true in 2018. In 2026, Expo supports production-scale applications with cloud builds, custom native modules, and the full New Architecture. Companies running Expo in production include apps with 120,000+ monthly active users reporting 99.2% crash-free session rates.

"React Native always performs better than Expo." Runtime performance is identical. Both run on the same Hermes engine, the same JSI bindings, the same Fabric renderer. The difference is in development tooling, not execution speed.

"Expo removes native capabilities." Expo provides native access through the Expo Modules API and config plugins. For cases that genuinely require direct access to the native project, the bare workflow retains full React Native capabilities while keeping Expo's SDK and services.

"You'll eventually need to eject from Expo." The concept of "ejecting" is largely obsolete. Expo's bare workflow and custom dev clients let you access native code without abandoning Expo's tooling. The line between managed and bare continues to blur.

What to Do Next

The decision between Expo and bare React Native should be made before you write the job description, not during the interview process. It determines who you hire, what you pay, and how fast you ship.

For most new projects in 2026, start with Expo. You'll ship faster, upgrade easier, and deploy OTA fixes in minutes instead of days. If you hit a limitation that requires bare workflow, Expo's bare workflow gives you the escape hatch without losing Expo's tooling.

For the full hiring process, including skills evaluation, interview questions, and onboarding, see our step-by-step hiring guide. For understanding what the New Architecture means for your project, see our New Architecture guide.

At Hire React Native Developers, every developer on our platform specifies their workflow expertise (Expo managed, Expo bare, or bare React Native) so you can match the right developer to your project from day one. Vetted senior developer in your team within 5 days, 2-week risk-free trial.

Hire a React Native Developer →

Keep reading

More articles

View all articles
Developer interview session with technical assessment notes and laptop.
19 min
May 27, 2026

React Native Developer Interview Questions That Actually Reveal Expertise

Most React Native interview question lists read like quizzes on framework documentation. "What is the Virtual DOM?" "Explain the component lifecycle." "What's the difference between state and props?" A candidate who memorized the docs for two days can answer all of these. A candidate who has shipped three production apps and debugged a Fabric threading issue at 2 AM can also answer them, but you've learned nothing about the gap between those two people.

  • React Native
  • Hiring
  • Interview Questions
Read article
Hiring manager reviewing React Native developer candidates and interview red flags.
18 min
May 27, 2026

7 Red Flags to Watch Out for When Hiring a React Native Developer

A bad React Native hire doesn't announce itself with a dramatic failure. It shows up slowly: a feature that takes three weeks instead of three days. A codebase that nobody else on the team can read. A "senior" developer who freezes the first time a native crash log appears in Xcode. An app that works on the simulator but crashes on 30% of real Android devices.

  • React Native
  • Hiring
  • Interviewing
Read article
React Native developer reviewing production mobile app code and performance tooling.
18 min
May 27, 2026

Must-Have Skills in a React Native Developer in 2026 (Beyond the Basics)

Every React Native job description asks for JavaScript, TypeScript, React, and state management. Every candidate claims to have them. And none of these tell you whether the developer can actually ship a product that works on a $200 Android phone, survives an iOS update cycle, and doesn't need a rewrite six months after launch.

  • React Native
  • Hiring
  • Developer Skills
Read article