EN: The “Expo vs. React Native CLI” debate is over, but not everyone has gotten the memo. The React Native team itself now recommends a framework-first approach for new projects. I wrote a deep-dive on why modern @Expo (with EAS, Config Plugins) is the default, production-ready choice for most teams in 2025. This isn’t the Expo you remember. #ReactNative #Expo #MobileDevelopment
The “Expo vs. bare React Native CLI” debate is one of the oldest traditions in the React Native community. For years, the conventional wisdom was simple: start with Expo for a hobby project, but for “serious” production work, you’ll eventually need the power and flexibility of the CLI.
That advice is now dangerously out of date.
The landscape has fundamentally shifted. The React Native core team itself no longer recommends starting new projects with react-native init. Instead, they advocate for using a framework. And in today’s ecosystem, Expo is, for most use cases, the superior choice for building production-grade applications. If you’re still starting projects with the bare CLI in 2025, you’re likely choosing a path with more friction, more manual maintenance, and fewer powerful features out of the box.
This isn’t about hype. It’s about a pragmatic look at the tools that solve real-world development problems. Let’s break down why.
The Game Has Changed: Why This Isn’t the Old “Walled Garden” Debate
The old argument against Expo was its “walled garden” approach. If you needed a third-party package with custom native code that wasn’t in the Expo SDK, you were stuck. Your only option was to eject, a one-way process that left you with a complex, often messy bare CLI project.
That entire paradigm is dead, thanks to two key innovations: Development Builds and Config Plugins.
Escape Hatches You Can Actually Use: Config Plugins
Config Plugins (also known as Continuous Native Generation or CNG) are the single most important feature that dismantled the “walled garden.” They are small JavaScript functions that run during the native build process, allowing you to modify native project files like Info.plist on iOS or AndroidManifest.xml on Android.
What does this mean in practice? You can integrate almost any third-party React Native library into your Expo project. The community has already created plugins for most popular packages.
Let’s say you need to set up different environments (staging, production) with unique API keys and app icons. Instead of manually managing Xcode projects or Gradle files, you create a dynamic configuration.
// app.config.ts
import { ExpoConfig } from 'expo/config';
// Define your environment-specific variables
const environments = {
development: {
name: 'MyApp (Dev)',
bundleIdentifier: 'com.myapp.dev',
apiKey: 'DEV_API_KEY',
},
staging: {
name: 'MyApp (Staging)',
bundleIdentifier: 'com.myapp.staging',
apiKey: 'STAGING_API_KEY',
},
production: {
name: 'MyApp',
bundleIdentifier: 'com.myapp.production',
apiKey: 'PROD_API_KEY',
},
};
// Get the current environment from an environment variable
const currentEnv = process.env.APP_VARIANT || 'development';
const envConfig = environments[currentEnv];
const config: ExpoConfig = {
name: envConfig.name,
slug: 'my-app',
ios: {
bundleIdentifier: envConfig.bundleIdentifier,
},
android: {
package: envConfig.bundleIdentifier,
},
// Use extra to pass environment variables to your app code
extra: {
apiKey: envConfig.apiKey,
eas: {
projectId: 'YOUR_EAS_PROJECT_ID'
}
},
};
export default config;
With this one file, you can generate distinct builds for each environment by simply setting an environment variable (APP_VARIANT=staging eas build). This eliminates a massive source of manual error and simplifies CI/CD pipelines immensely.
Production-Grade Superpowers, Out of the Box
Choosing Expo isn’t just about avoiding problems; it’s about gaining capabilities that are difficult and time-consuming to build yourself.
Ship Critical Fixes Instantly with Expo Updates (OTA)
Your app is in production. A critical bug that breaks the login flow is discovered. With a traditional CLI workflow, your fix is at the mercy of the App Store and Google Play review times, which can take hours or even days.
With Expo Application Services (EAS) Update, you can push the JavaScript-only fix directly to your users’ devices in minutes. It’s an Over-the-Air (OTA) update that bypasses the store review process for JS bundle changes.
[Here, a flowchart would illustrate the OTA update process: 1. Developer pushes JS code to EAS. 2. EAS builds the JS bundle. 3. User opens the app. 4. The app checks EAS for a new bundle. 5. The new bundle is downloaded in the background. 6. The next time the user opens the app, they’re running the patched code.]This isn’t just a convenience; it’s a powerful tool for risk management and rapid iteration. It makes your team more agile and your application more resilient.
A Build Process That Just Works
Ask any developer who has maintained a bare React Native project: managing native build environments is a headache. Keeping Xcode, CocoaPods, Android Studio, and Gradle versions in sync across a team and a CI server is fragile and time-consuming.
EAS Build abstracts this away completely. It provides a consistent, managed, and cloud-based build environment for your app. You run a single command, and it queues up a build for you. This frees your team from being native build experts and lets them focus on writing features.
A Reality Check: What Are the Downsides?
No tool is perfect. The most common argument I hear against Expo today is, “It just adds more dependencies.”
And it’s true. Expo is a framework; it provides a curated set of libraries and services. But this is a trade-off. You’re trading direct control over every single dependency for a cohesive, well-maintained ecosystem where upgrades are smoother and packages are designed to work together. With the bare CLI, you’re not avoiding dependencies; you’re just becoming the sole maintainer of your own, hand-picked “framework.”
For a highly specialized app—perhaps a social media app with complex, custom native video processing where you use zero Expo libraries—the overhead might not be worth it. But for the 95% of applications (e-commerce, internal tools, productivity, content apps), the benefits of the managed ecosystem far outweigh the cost of a few extra packages in your node_modules.
The Verdict: It’s a Question of Focus
The decision is no longer about “capability” vs. “limitation.” It’s about where you want to spend your team’s time.
Do you want to become an expert in configuring Xcode build settings, managing Gradle dependencies, and building your own OTA update mechanism? Or do you want to focus on building features, shipping faster, and delivering a better product?
By handling the undifferentiated heavy lifting of builds, configuration, and updates, Expo allows you to focus purely on the application itself. The escape hatches are there if you need them, but they are the exception, not the rule. The React Native team’s official guidance confirms this direction. For your next production project, the question isn’t “Why Expo?” but rather, “Do I have a very specific, compelling reason not to use Expo?”
For those who have already made the switch from CLI to Expo, what was the “aha!” moment for you? And for anyone still on the fence, what’s the biggest question left in your mind?
Kudos to the teams at @Expo and @React Native for pushing the ecosystem forward. Folks like @charlie Cheever and @brent Vatne have shared great insights on this topic.
ReactNative #Expo #React #MobileDevelopment #DeveloperExperience #JavaScript #AppDevelopment #CrossPlatform #EAS