skip to content

In an Expo project, what does npx expo prebuild do, and why are the ios and android folders usually left out of git?

level: juniorimportance: must knowfreq 58%

answer

  1. native projects as build output
  2. template for the installed SDK
  3. app config plus config plugins
  4. autolinking native modules from package.json
  5. /ios and /android in .gitignore

basics

~20 s

npx expo prebuild generates the ios and android native projects from the SDK's template, the app config and its config plugins, plus autolinked modules. Because they can be regenerated at any time, they are treated as build output and gitignored.

solid answer

~40 s

`npx expo prebuild` is Expo's implementation of **Continuous Native Generation**: it copies the native template that matches the installed `expo` version, applies the app config (`app.json` or `app.config.ts`) and every config plugin to it, and relies on autolinking to wire native modules from `package.json`, then installs pods on macOS. The resulting `ios/` and `android/` folders are a function of those inputs, so the source of truth is the config, not the native files. New projects therefore list `/ios` and `/android` in `.gitignore`; EAS Build and `npx expo run:ios` / `run:android` generate them when they are missing. Edits made by hand inside those folders are not part of the inputs, so the next regeneration loses them.

code

bash · 5 lines
bash
# Generate both native projects from app config, plugins and the SDK template
npx expo prebuild

# Generate only Android, without installing dependencies or pods
npx expo prebuild --platform android --no-install

go deeper

for a junior

Recall that prebuild generates ios and android from the app config, plugins and SDK template, and that these folders are gitignored build output.

for a middle

Explain the five inputs, when EAS Build and run commands trigger prebuild, and the package.json side effects prebuild still has.

for a senior

Show how you keep a team from hand-editing generated folders and how you move a needed native change into a package, config field or plugin.

for a principal

Frame CNG as a maintenance strategy: what the team stops owning, what it must now own as plugins, and when that trade stops paying.

## The idea: native projects as build output A React Native app needs an Xcode project and a Gradle project before it can compile. The traditional approach creates them once and then maintains every change by hand for the life of the app. **Continuous Native Generation (CNG)** turns that around: the native projects are **short-lived artifacts**, generated when you build or debug, from a small set of inputs you do maintain. In Expo, the command that performs the generation is **`npx expo prebuild`**. ## What prebuild combines 1. The **prebuild template** for the installed version of the `expo` package, which also pins the matching React Native version. 2. The **app config**: `app.json`, or `app.config.ts` when values must be computed. 3. **Config plugins** listed in the app config, which modify the template (permissions in `Info.plist` and `AndroidManifest.xml`, entitlements, Gradle properties and so on). 4. **Autolinking**, which links native modules found in `package.json` without manual edits. 5. **Arguments** to the command itself, such as `--platform`. The output is two ordinary native projects, `ios/` and `android/`, ready for Xcode and Gradle. On macOS it also runs `pod install` unless you pass `--no-install`. ## Why the folders stay out of git | If the native folders are... | Source of truth | What a native change looks like | |---|---|---| | **generated and gitignored** | app config, plugins, installed packages | a config or package change, then regenerate | | **committed and edited** | the native files themselves | a hand edit you maintain forever | Keeping them out of git has concrete benefits: - **No drift**: nobody can make a hand edit that silently disagrees with the config. - **Small diffs**: a new permission is one line in the app config instead of edits across two platforms. - **Easier upgrades**: a new SDK brings a new template; regenerate instead of merging native diffs. - **No orphaned code**: removing a library removes its plugin, and the next generation leaves nothing behind. New projects from `create-expo-app` already list `/ios` and `/android` in `.gitignore`. ## Who runs prebuild for you - **EAS Build** runs prebuild before compiling when the uploaded project has no `android` and `ios` folders. Because gitignored folders are not uploaded, a CNG project always gets fresh native projects in the cloud. - **`npx expo run:ios` and `npx expo run:android`** run prebuild for that platform **only when its folder is missing**. If the folder exists, they build what is there, so after changing the app config you run `npx expo prebuild` yourself. ## What prebuild touches besides the native folders Prebuild is not perfectly side-effect free. The documented side effects are: - it rewrites `package.json` **scripts** that call `expo start --android` / `--ios` to `expo run:android` / `run:ios`; - it can update **dependencies** in `package.json` when they differ from what the template expects (skip specific packages with `--skip-dependency-update`). Both are safe to commit, which keeps later regenerations quiet. ## The rule that follows If a change is not expressed in the inputs, it does not exist. Editing `ios/Podfile` or `AndroidManifest.xml` directly works until the next regeneration; since Expo SDK 57 a plain `npx expo prebuild` deletes and recreates the folders by default. The durable place for a native change is a package, an app config field or a config plugin. ## Common misunderstandings - **"Prebuild is an eject."** It is not a one-way door. Running it produces folders you can delete and regenerate at will; ownership only changes if you commit and hand-edit them. - **"Prebuild is only for EAS."** Local development builds use the same command, either directly or through `npx expo run:ios` / `run:android` when a folder is missing. - **"Generated means untouchable."** You can open the generated projects in Xcode or Android Studio to debug or profile; you just do not keep changes there. - **"Web needs prebuild too."** It does not: there is no native project for the web, which is why prebuild supports Android and iOS only. A quick self-test for a team: delete both folders, run `npx expo prebuild`, and build. If the app still works exactly as before, the project is truly CNG.

  • In an Expo CNG project, does npx expo run:ios pick up an app.json change on its own?
    Only if `ios/` does not exist yet, because `run:ios` prebuilds a platform only when its folder is missing. With an existing folder it builds the old native project, so run `npx expo prebuild` (clean by default since SDK 57) before rebuilding.
  • How does EAS Build treat a CNG project whose native folders are gitignored?
    The folders are not uploaded, so EAS Build sees a project without `android` and `ios` and runs prebuild itself before compiling. A project that uploads native folders is built as-is, and EAS does not run prebuild over them.

The native folders are like a printed report generated from a spreadsheet: you fix numbers in the spreadsheet and print again, because notes scribbled on the printout disappear the next time anyone prints.

saying these in an interview costs you the question

  • Prebuild is a one-time eject after which the folders are yours
  • Hand edits inside ios/ survive the next prebuild
  • Prebuild only ever writes the ios and android folders
  • npx expo run:android regenerates the native project on every run
  • The native folders must be committed for EAS Build to work