skip to content

Continuous Native Generation

npx expo prebuild regenerates ios/ and android/ from app config and plugins, so native folders stay out of git. Interviewers ask what changes when a team commits them and goes bare.

part ofExpo (React Native)overview, primer and where to startread it →
on this pageshow

explore

questions

5

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
open as a page

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%

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.

open as a page

How does upgrading the Expo SDK differ between a project using Continuous Native Generation and one that commits its native folders?

level: middleimportance: should knowfreq 42%

basics

~20 s

With CNG you bump expo and its packages, then regenerate the native folders from the new SDK's template. With committed native folders you must apply the native template changes yourself, using Expo's native upgrade helper, then reinstall pods.

open as a page

Your managed Expo habit-tracking app needs a native camera SDK with iOS and Android permission entries; how do you add it without committing ios and android?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Install the library with npx expo install, add its config plugin and permission strings to the app config, then regenerate with npx expo prebuild or let EAS Build do it, and ship a new native build. The native folders stay generated.

open as a page

An Expo team wants to commit ios and android and edit them directly instead of using Continuous Native Generation; how do you decide, and what does it cost?

level: principalimportance: should knowfreq 30%

basics

~20 s

Commit native folders only when needed native changes cannot reasonably be expressed as packages and config plugins. The cost is owning both native projects: manual SDK upgrades, app config native fields no longer applied, and drift nobody regenerates away.

open as a page