Prebuild Best Practices
A condensed summary of the 25 most important best practices drawn from every page in CNG & Prebuild - never hand-edit generated files you cannot reproduce.
Search across all documentation pages
A condensed summary of the 25 most important best practices drawn from every page in CNG & Prebuild - never hand-edit generated files you cannot reproduce.
Treat CNG as the default on SDK 57: Maintain app.config.ts, config plugins, and modules/ - not hand-edited ios/ and android/ folders.
Gitignore native directories: Add ios/ and android/ to .gitignore unless a brownfield ADR explicitly requires committed native projects.
Understand clean regen default: npx expo prebuild deletes existing native folders before generating - pass --no-clean only during controlled migration work.
Never edit generated files you cannot reproduce: If a change is not in config or a plugin, it will be lost on the next clean prebuild.
Express permissions in plugins: NSCameraUsageDescription, Android permissions, and entitlements belong in the plugins array - not post-prebuild Xcode edits.
Write idempotent config plugins: Running prebuild twice must not duplicate manifest entries or Gradle apply lines - existence checks before every add.
Document plugin order in app.config.ts: Multiple plugins touching the same native file depend on execution order - comment the rationale.
Prefer library config plugins: expo-camera, expo-notifications, and Expo modules ship maintained SDK 57 plugins - do not reinvent them.
Use npx expo install for native dependencies: Manual npm install can pull JS versions incompatible with SDK 57 native binaries.
Run expo-doctor before prebuild: Catch invalid config and version skew before wasting a compile cycle on EAS or locally.
Use npx expo config --type introspect while developing plugins: Inspect mod results without full native generation on every save.
Port brownfield native diffs to plugins before cutover: Inventory every Xcode/Gradle change vs fresh prebuild - spreadsheet with owners per row.
Commit modules/ for custom native code: Local Expo modules are source of truth; generated ios/MyApp/ is ephemeral under CNG.
Use expo run:ios / expo run:android for local native iteration: Wraps prebuild + compile - rebuild after Swift/Kotlin changes; Fast Refresh does not update native code.
Rebuild dev clients after SDK bumps: expo-dev-client embeds native runtime - OTA cannot upgrade it after an SDK or native dependency change.
Delete stale native folders on upgrade: After npx expo install --fix on a new SDK, rm -rf ios android before prebuild - old templates target the previous RN version.
Upgrade SDKs sequentially: One SDK per PR - prebuild templates and plugin APIs change per release; skipping versions compounds pain.
Commit lockfiles with every native-affecting change: CI, EAS, and fingerprint hashes must resolve the same dependency tree as local dev.
Adopt fingerprint gates in CI: @expo/fingerprint detects when a new native build is required - do not ship OTA to incompatible binaries.
Use runtimeVersion.policy: "fingerprint" for CNG OTA: Ties update eligibility to native inputs, not semver alone when plugins change between store releases.
Let EAS Build run prebuild in CI: Upload config + JS - not generated native folders - unless brownfield migration is incomplete.
Map eas.json profiles to environments: development, preview, and production must align credentials and env vars with fingerprint and build jobs.
Physical device smoke test after plugin merges: Simulators miss push entitlements, App Links, and signing issues that prebuild alone cannot catch.
Pair native work with Native Module Rules: Search Expo SDK packages and config plugins before writing Swift/Kotlin - custom modules need ADRs and owners.
Schedule brownfield CNG migration before the next major SDK: Manual native merge cost grows every release - cut over when audit spreadsheet is complete and clean prebuild passes.
eas update.eas build for production and development clients.app.config.tsnpx expo config --type introspectnpx expo prebuild --no-install twice - zero diff on second runexpo run:ios or EAS development build for device proofStack versions: This page was written for React 19.2.3, React Native 0.86.0, and Expo SDK 57 (
expo~57.0.4).
Reviewed by Chris St. John·Last updated Jul 16, 2026