In an Expo project, a config plugin option was changed but the next build still has the old native value; what are the likely causes, and how do you fix it?
answer
- mods only run at prebuild
- committed native folders skip prebuild
- run:* generates only when missing
- without --clean changes are layered
- rebuild the development build
basics
~20 sThe change never passed through prebuild: native folders were committed so EAS Build skipped prebuild, stale local folders were reused, or prebuild ran without --clean. Run npx expo prebuild --clean, check the file, and rebuild.
solid answer
~40 sPlugin mods run only at prebuild, so I look for whatever skipped it or reused an old native project. If `android` and `ios` are committed, EAS Build does not run prebuild and the plugin change is ignored in CI. Locally, `npx expo run:*` generates native folders only when they are missing, and prebuild without `--clean` layers edits on existing files, where non-idempotent plugins duplicate entries and removed plugins leave traces. And a JavaScript update or an old development build cannot carry native changes. The fix: `npx expo prebuild --clean`, inspect the generated file such as `android/gradle.properties`, rebuild the binary, and keep native folders out of git so EAS Build regenerates them every time.
code
bash · 3 linesnpx expo config --type prebuild
npx expo prebuild --clean
git status --short android iosgo deeper
Recall that plugin changes need prebuild and a new native build before they show up.
Explain which workflows skip prebuild, what --clean changes, and why incremental prebuilds can drift from clean ones.
Diagnose from the build logs and generated files, fix the workflow so CI always regenerates, and treat the change as a new native version.
Decide and document whether native folders are generated or committed, since mixing the two is the root of this class of bug.
## The symptom A team changes a config plugin entry, for example raising `minSdkVersion` through `expo-build-properties` or editing a maps plugin's API key option, and the next build still carries the old value. Nothing is wrong with the plugin. The change never reached the native project. A config plugin's **mods** run only when **prebuild** generates or updates the native projects. Anything that skips prebuild, or reuses an old native project, keeps the old value. ## The usual causes | Cause | What happens | |---|---| | `android` and `ios` folders are committed | EAS Build does not run prebuild, to avoid overwriting your native changes | | Local folders from an earlier prebuild | `npx expo run:*` generates native folders only when they are missing | | Prebuild ran without `--clean` | Changes are layered on top of existing files, which may not match a fresh result | | Only JavaScript was shipped | An update or a Metro reload cannot change native files | | The installed development build is old | The device still runs a binary built before the change | ## Why committed native folders change everything In a **CNG** project, the `android` and `ios` folders are generated artefacts, usually git-ignored. EAS Build sees they are absent and runs prebuild, so plugin changes flow into every build. Once those folders are committed, the project behaves like a bare project: EAS Build treats them as the source of truth and does **not** run prebuild. A plugin change then does nothing in CI until someone regenerates the folders and commits the result. Teams that half-migrate end up with a `plugins` array that looks authoritative but no longer is. ## Why `--clean` matters Prebuild without `--clean` applies mods on top of the existing native files. That is faster, but: - **Some plugins are not idempotent.** A plugin that appends text, such as a Gradle snippet or a manifest entry, can add it a second time. - **Removed plugins leave traces.** Deleting an entry does not undo edits it made to files that already exist. - **Stale values survive.** A value written by an older configuration can remain where no current mod overwrites it. `npx expo prebuild --clean` deletes the native folders and regenerates them from the app config, so the result depends only on the current configuration. It warns about uncommitted git changes first. ## A reliable routine after a plugin change 1. Change the plugin entry or its options in the app config. 2. Run `npx expo prebuild --clean` and inspect the generated file, such as `android/gradle.properties`, to confirm the new value. 3. Rebuild the native app: a new development build for the team, a new store build for users. 4. In CI, keep native folders out of git so EAS Build regenerates them on every build. To see what prebuild will receive, `npx expo config --type prebuild` prints the evaluated config, including plugin options. ## Production judgement - **Native changes change compatibility.** A binary built with the new plugin settings is a new native version; JavaScript updates published for older binaries must not rely on it. Expo's runtime-version policies exist to encode that boundary. - **Decide the workflow explicitly.** Either the folders are generated (plugins are the source of truth) or they are committed (native files are). Mixing both is where "the plugin didn't apply" bugs come from. - **Make plugins safe to re-run.** Your own plugins should check before inserting, so an incremental prebuild produces the same result as a clean one. This behaviour is documented for Expo SDK 57; EAS Build's rule of skipping prebuild when native folders exist is part of the CNG workflow.
- Why can re-running prebuild without --clean give a different result from a clean prebuild?Without `--clean`, prebuild applies mods on top of the existing native files. Plugins that append text can add it twice, edits from plugins you have since removed stay in place, and old values survive where no current mod overwrites them. A clean prebuild deletes the folders and regenerates them, so the output depends only on the current app config.
- The team commits android and ios but also keeps a plugins array. What is the risk?The plugins array looks authoritative but no longer drives builds, because EAS Build skips prebuild when native folders exist. A plugin change silently does nothing until someone regenerates and commits the folders, and a hand edit to those folders can be wiped by that regeneration. Pick one source of truth: generated folders driven by plugins, or committed folders edited directly.
saying these in an interview costs you the question
- EAS Build always runs prebuild, even with committed native folders
- npx expo run:android re-runs prebuild on every build
- Removing a plugin undoes its edits on the next incremental prebuild
- An EAS Update can apply a plugin change to installed apps
- Restarting Metro picks up new plugin options