--- name: expo-managed-vs-bare description: Default to Expo managed workflow; drop to bare only for capabilities Expo hasn't shipped. Expo SDK 54+ (RN 0.86) baseline. when_to_use: Starting or expanding an Expo/React Native app. Mobile team pins. tags: [mobile, expo, react-native, versioned] --- # Expo managed vs bare (SDK 54+, RN 0.86) Anchored to **Expo SDK 54** (2026 line) shipping React Native 0.86 as of 2026-07. New Architecture is default; JSC is removed; Hermes-only. ## Managed by default - One codebase, `expo prebuild` on demand to regenerate iOS/Android projects. - OTA updates via EAS Update. - Config Plugins system covers 95% of native customization without ejecting. ## When to drop to bare - Custom native modules with no Expo Module wrapper (rare — Expo Modules API covers most cases now). - Fork of React Native itself. - Vendor SDK that ships only a `.aar` or `.xcframework` with a config that Expo Config Plugins can't express. If none of the above, stay managed. `expo prebuild` gives you the native project when you need to poke it, without permanent ejection. ## SDK version alignment - Expo SDK N ships against a specific RN version. Don't `yarn add react-native@latest` — use `expo install react-native` so you get the version the SDK bundles. - Expo SDK is released ~quarterly; RN follows ~monthly. Follow Expo's calendar, not RN's. - Native modules published to npm should declare `expo-modules-core` peer dep with a range covering the current + previous SDK. ## Config Plugins - `app.config.ts` over `app.json` — you get typing + can compute values (env-branched dev/staging/prod). - Plugins compose: `[plugins]: ["expo-notifications", ["./plugins/withCustomEntitlement", { key: "..." }]]`. - Custom plugins live in `plugins/` and are typed by `ConfigPlugin`. ## EAS Build - Two profiles minimum: `development` (dev client, sim + device), `production` (store submission). - `eas.json` per-branch overrides for env vars; secrets go into EAS Secrets, never repo. - **Fingerprint** (SDK 51+) determines rebuild need — if your JS-only change fingerprints identically, no native rebuild. Saves 15+ min per iteration. ## When Bare IS the answer - Deep customization of the launcher / splash screen beyond `expo-splash-screen` capabilities. - Multi-target apps (main + share extension + widget) where Expo's single-target model doesn't fit. - Enterprise MDM constraints that require unusual entitlements. If you go bare, keep the Expo dep chain (`expo`, `expo-modules-core`) — you still benefit from `expo-router`, `expo-notifications`, etc. Bare ≠ vanilla RN. ## Anti-patterns - **Ejecting to bare because a StackOverflow answer said so.** Search for a Config Plugin first. - **Mixing `react-native init` with `expo` deps.** Pick one project generator; don't try to retrofit. - **Locking to a beta SDK for production.** Expo betas are for testing your app against the next SDK, not for shipping.