React Native Expo

Use this stack when Android and iOS both matter, React is already the fastest team skill, and the app can live inside the React Native ecosystem.

Good fits:

  • Cross-platform product apps that need native distribution without two fully separate native codebases.
  • Content, workflow, marketplace, media, fitness, education, and internal apps with normal device APIs.
  • Solo-dev apps where Expo Router, config plugins, EAS Build, EAS Submit, and EAS Update remove native release toil.
  • Apps that need a realistic path from prototype to app-store release without abandoning React.

Weak fits:

  • Apps whose core value depends on deep platform APIs with no maintained React Native or Expo module.
  • Permission-heavy apps where store policy evidence is uncertain.
  • Graphics, games, low-latency media, or always-on background apps where native control matters more than shared UI.
  • Teams that cannot test real Android and iOS builds before release.

Stack Shape

LayerDefaultWhy
App frameworkExpoReact Native framework, SDK packages, CLI, app config, prebuild, and development loop
UI runtimeReact NativeNative Android/iOS views driven by React
RoutingExpo RouterFile-based routes, layouts, route groups, dynamic routes, typed routes, deep links, and web support
Development binaryDevelopment build with expo-dev-clientProject-specific native shell that supports native dependencies and app config
Native generationCNG with npx expo prebuildRegenerates android/ and ios/ from config, plugins, dependencies, and SDK templates
Native customizationConfig plugins and Expo Modules APIDurable native changes that survive prebuild and rebuilds
Build and submitEAS Build and EAS SubmitSigned Android/iOS binaries and store uploads without hand-maintaining local signing flows
OTA updatesEAS UpdateJavaScript, style, and asset updates inside a compatible runtime version
QualityJest, React Native Testing Library, router tests, built-app smoke, store-track checksMobile failures hide in native builds, devices, permissions, and store release paths

Minimum Done Criteria

Do not call an Expo feature done until these are true:

AreaProof
Dependenciesnpx expo install --check or equivalent compatibility review has no unexplained drift
JavaScript testsJest and React Native Testing Library tests pass for changed logic and screens
RouterRoute files, layouts, params, and typed links are exercised by tests or a manual route smoke
Native boundaryAny native dependency, permission, plugin, SDK, app config, or module change has a rebuild plan
Development buildA development or preview build is installed when the change touches native behavior
Android releaseA production AAB can be built or the blocker is documented
iOS releaseA production IPA or TestFlight path is confirmed on a Mac/EAS path or the blocker is documented
OTA safetyRuntime version, branch, channel, rollout, and rollback path are known before eas update
PermissionsAndroid manifest, iOS usage strings, denied flows, and store explanations are checked
SecretsNo signing keys, service account keys, API secrets, provisioning assets, or EAS tokens are committed

First Pages To Read

Start with one Expo app, Expo Router, TypeScript, a small src/ layer for API/hooks/state/components, app.config.ts or app.json, and EAS profiles for development, preview, and production.

Use Expo Go only for early learning and JavaScript-only prototypes. Move to a development build as soon as you add native libraries, custom permissions, app config, release signing, or anything store-bound.

Gotcha: EAS Update is not a release escape hatch. Native code, native dependencies, permissions, Expo SDK upgrades, app identifiers, and runtime-incompatible changes require a new native binary.