skip to content

Since Expo SDK 57, what does npx expo prebuild do to existing native folders by default, and when would you pass --no-clean?

level: middleimportance: should knowfreq 38%

answer

  1. delete, then regenerate
  2. only the platforms selected
  3. --no-clean layers onto existing files
  4. non-idempotent dangerous modifiers
  5. EXPO_NO_GIT_STATUS=0 restores the check

basics

~20 s

Since SDK 57, npx expo prebuild deletes the existing ios and android folders it targets and regenerates them. Pass --no-clean only for a quick re-sync you accept may differ from a fresh generation, such as testing a plugin change.

solid answer

~40 s

In Expo SDK 57 the CLI made clean the default: `npx expo prebuild` deletes the selected platform folders and generates them again from the template, app config and plugins, which is the reproducible result a CI or EAS build would get. `--no-clean` applies the config on top of the existing folders instead. It is faster, but plugins that are not idempotent, especially regex-based dangerous modifiers, can apply twice or leave stale edits, so the result may not match a fresh generation. Two safety details: the delete only touches the platforms you select with `--platform`, and the uncommitted-changes prompt is off by default since SDK 55 because `EXPO_NO_GIT_STATUS` defaults to true; set `EXPO_NO_GIT_STATUS=0` to get the warning back. So any hand edit in those folders is lost without a prompt.

code

bash · 11 lines
bash
# SDK 57 default: delete and regenerate ios/ and android/
npx expo prebuild

# Regenerate Android only; ios/ is untouched
npx expo prebuild --platform android

# Layer config onto existing folders (faster, may drift)
npx expo prebuild --no-clean

# Opt back into the uncommitted-changes warning
EXPO_NO_GIT_STATUS=0 npx expo prebuild

go deeper

for a junior

Recall that since SDK 57 a plain prebuild deletes and regenerates the native folders, and that --no-clean keeps them.

for a middle

Explain why layering drifts: non-idempotent plugins, leftovers from removed config, and a template that is not refreshed.

for a senior

Show the safety habits: know the git-status check is off by default, scope runs with --platform, and finish plugin iteration with a clean run that matches CI.

for a principal

Weigh local iteration speed against reproducibility, and decide which workflows may ever use --no-clean on a shared project.

## The change in SDK 57 Before Expo SDK 57, a plain `npx expo prebuild` **layered** the config onto whatever `ios/` and `android/` already contained, and you passed `--clean` to delete and regenerate. The `@expo/cli` 57.0.0 release flipped that: prebuild now **clears and regenerates the native folders by default**, and **`--no-clean`** opts back into layering. The CLI still accepts `--clean`, which is now simply the default behaviour. Parts of Expo's CNG guide still describe the old default, so read the CLI help or changelog when the two disagree. ## What a default (clean) run does 1. Reads the app config and works out which platforms to generate (`--platform ios|android|all`, default all, filtered by the config's `platforms`). 2. If folders for those platforms **already exist**, runs its guards, then **deletes those folders only**. A first-ever prebuild skips this step. 3. Copies the template for the installed SDK and applies app config and plugins. 4. Installs dependencies and CocoaPods unless `--no-install` is passed. The guards before deletion are narrower than many people assume: - **Native-module guard**: if the project looks like a native library rather than an app, prebuild bails out instead of erasing its native code. - **Git-status guard**: controlled by `EXPO_NO_GIT_STATUS`, which **defaults to true since SDK 55**, meaning the dirty-tree check is skipped. Set `EXPO_NO_GIT_STATUS=0` to be warned and asked about uncommitted changes. In a non-interactive terminal, such as CI, a dirty tree only produces a warning and the command continues. ## What --no-clean does, and why it drifts With `--no-clean`, prebuild keeps the existing folders and applies the config to them again. That is faster, and it keeps anything the config does not touch. The cost is **reproducibility**: - **Non-idempotent plugins**: a plugin that appends a line or runs a regex replacement can apply twice, duplicating entries. - **Removed config stays behind**: deleting a plugin or a permission from the app config does not remove what an earlier run wrote. - **Template changes are missed**: the base native project is not refreshed. | | default (clean) | `--no-clean` | |---|---|---| | Existing folders | deleted, then regenerated | kept, config reapplied | | Matches EAS/CI output | yes | not guaranteed | | Hand edits | lost | kept, possibly clashing | | Speed | slower | faster | | Removed plugins | fully removed | leftovers may remain | ## When --no-clean is reasonable - **Iterating on a config plugin** locally, when a full regeneration and pod install on every attempt is too slow. Finish with a clean run before trusting the result. - **Temporary native experiments**: you added a file in Xcode to try something and want to reapply the config without losing it for the moment. The experiment still has to move into a plugin or module before it can survive. It is not a way to keep permanent hand edits; under CNG those belong in a plugin. ## Limiting the blast radius - **`--platform android`** regenerates only Android and leaves `ios/` alone, which also avoids a slow pod install. - On **Windows**, prebuild skips iOS generation with a warning, because the iOS project needs macOS or Linux to generate. - If `ios/` or `android/` hold anything you have not captured in config, commit or copy it first; with the default settings nothing will ask you. ## A safe local routine on SDK 57 1. Commit or stash your JavaScript and config changes, so a surprise is easy to inspect. 2. Change the inputs: package, app config or plugin. 3. Run `npx expo prebuild`, or `npx expo prebuild --platform ios` when only one side changed. 4. Build and run the affected platforms. 5. If you used `--no-clean` while iterating, finish with a default clean run and build again before opening a pull request. For teams that want the old safety net, exporting `EXPO_NO_GIT_STATUS=0` in the shell profile restores the prompt on every interactive run without changing the project.

  • A developer ran npx expo prebuild on SDK 57 and lost an uncommitted edit in ios/ with no prompt; why?
    Clean is the default in SDK 57, and the git-status check is skipped by default because `EXPO_NO_GIT_STATUS` defaults to true since SDK 55. Setting `EXPO_NO_GIT_STATUS=0` restores the warning and confirmation on a dirty working tree.
  • Why can npx expo prebuild --no-clean give a different result from EAS Build?
    EAS Build generates fresh folders for a CNG project, while `--no-clean` reapplies config to folders that may hold earlier plugin output, removed permissions or hand edits. Non-idempotent plugins can also apply twice, so the local project no longer matches a clean generation.

saying these in an interview costs you the question

  • You still need --clean to wipe the native folders on SDK 57
  • Prebuild always asks before deleting uncommitted native changes
  • --no-clean is the safe choice because it keeps your edits
  • A clean prebuild deletes both platforms even with --platform ios
  • Removing a plugin from app.json removes its edits under --no-clean