skip to content

In an Expo project, what does an entry in the app config's plugins array do, and when do its changes reach the installed app?

level: juniorimportance: should knowfreq 45%

answer

  1. native settings without a config field
  2. string or [name, options]
  3. function runs at config evaluation
  4. mods write files at prebuild
  5. needs a new native build

basics

~20 s

Each plugins entry names a config plugin, a Node.js function that edits the Expo config and registers native file changes. Those changes are written at prebuild, so they reach users only in a new native build, never through a JavaScript update.

solid answer

~40 s

A `plugins` entry references a **config plugin**, either a string (a package name or a local path) or a `[name, options]` tuple. The plugin is a Node.js function that receives the Expo config, can change it, and registers **mods** that edit native files such as `AndroidManifest.xml` or `Info.plist`. The function runs whenever the config is evaluated, but the mods run only at **prebuild**, locally or in EAS Build. So a plugin change needs prebuild plus a **new native build**; an over-the-air update, Fast Refresh or Expo Go cannot deliver it. In a CNG project this is how native settings stay reproducible instead of being hand-edited in folders that prebuild regenerates.

code

json · 9 lines
json
{
  "expo": {
    "plugins": [
      ["expo-location", { "locationWhenInUsePermission": "Show nearby drop-offs" }],
      ["expo-build-properties", { "android": { "minSdkVersion": 26 } }],
      "./plugins/withDeliveryZones.js"
    ]
  }
}

go deeper

for a junior

Recall what a plugins entry looks like, a string or a [name, options] tuple, and that it configures native settings the app config lacks.

for a middle

Explain the two phases, function at config evaluation and mods at prebuild, and why a change needs a new native build.

for a senior

Connect plugins to release practice: plugin changes are native releases, and OTA updates for older binaries must not depend on them.

for a principal

Weigh CNG with plugins against committed native folders for the team: reproducibility and upgrades versus direct control of native files.

## The problem config plugins solve An Expo project that uses **Continuous Native Generation (CNG)** does not keep hand-edited `android` and `ios` folders. `npx expo prebuild` generates them from the **app config** (`app.json` or `app.config.ts`) and the installed packages. That raises a question: where do native settings go that the app config has no field for, such as a maps SDK's API key in `AndroidManifest.xml` or a usage description in `Info.plist`? The answer is a **config plugin**: a JavaScript function that receives the Expo config and returns a modified one, optionally registering **mods**, which are edits to specific native files. A plugin is referenced in the app config's `plugins` array, and prebuild applies it every time it generates the native projects. ## What an entry in `plugins` looks like Each entry takes one of two forms: 1. **A string**: a package name such as `"expo-camera"`, or a path to a local file such as `"./plugins/withDeliveryZones.js"`. 2. **A tuple** `[name, options]`: the same reference followed by an options object passed to the plugin as its second argument. ```json { "expo": { "plugins": [ ["expo-location", { "locationWhenInUsePermission": "Show nearby drop-offs" }], ["expo-build-properties", { "android": { "minSdkVersion": 26 } }], "./plugins/withDeliveryZones.js" ] } } ``` A tuple must have one or two elements. Options written as a bare object, without the surrounding brackets, fail with an error that tells you to wrap the plugin configuration in square brackets. ## When the changes happen A plugin has two phases, and confusing them is the most common beginner mistake: - **Config evaluation.** The plugin function itself runs whenever Expo CLI evaluates the app config, not only during prebuild. Anything it sets on the config object directly happens here. - **Prebuild.** The **mods** it registers run only during `npx expo prebuild` (or the prebuild step that `npx expo run:android`, `npx expo run:ios` and EAS Build perform). That is when native files are actually written. So a change to a plugin entry reaches the device only after three things: prebuild regenerates the native project, the native app is **rebuilt**, and the new binary is installed. | Change | Delivered by | |---|---| | A screen or JavaScript logic | Fast Refresh in development, or an update in production | | A config plugin entry or its options | Prebuild plus a new native build | | A new native library | Prebuild plus a new native build | ## What config plugins are not - **Not runtime code.** They run in Node.js on your machine or build server, never on the device. - **Not a way to ship native changes over the air.** An update delivers JavaScript and assets; a new permission string or Gradle setting needs a new binary. - **Not available in Expo Go.** Expo Go is a prebuilt app with a fixed native side, so a project that depends on custom plugins needs a development build. - **Not a replacement for native code.** They edit project files; code that calls a native SDK still lives in a native module. ## Where plugins come from - **Libraries.** Many packages ship a plugin, for example `expo-location` for its permission strings. Installing with `npx expo install` usually adds such a package to `plugins` for you when it ships a plugin file. - **`expo-build-properties`**, a plugin dedicated to native build values such as the Android `minSdkVersion`. - **Your own local plugins**, for settings no library covers. ## Why interviewers ask For a delivery app adding a maps SDK, the interviewer wants to hear that you would not open Android Studio and edit the manifest by hand. In a CNG project that edit disappears the next time prebuild regenerates the folder. Declaring it through a plugin makes the native project **reproducible**: any machine or CI job can regenerate it from the app config. This is the model in Expo SDK 57, where CNG is the default for new projects.

  • Can an Expo update deliver a change made through a config plugin?
    No. An update ships JavaScript and assets for an existing binary. Config plugins change native project files, such as the manifest, `Info.plist` or Gradle properties, and those only reach users in a newly built binary. Treat any plugin change as a native release, and make sure updates published for older binaries do not depend on it.
  • Why does a project that adds a custom config plugin stop working in Expo Go?
    Expo Go is a prebuilt app whose native side is fixed; it never runs your prebuild. A setting written by your plugin, such as a maps SDK key in the manifest, does not exist inside Expo Go. The project needs a development build, which is generated from your app config and plugins and then compiled.
  • What is the difference between a plugin changing the config object and a plugin registering a mod?
    Changes to the config object happen when the plugin function runs, which is every time Expo CLI evaluates the app config, not only at prebuild. Mods are deferred edits to native files that run only during prebuild. Expo's guidance is to make config-object changes outside mods, so they apply even when prebuild does not run.

A config plugin is a line in a building's blueprint, not a repair on the finished house: changing it does nothing until the house is rebuilt from the updated plans.

saying these in an interview costs you the question

  • Config plugins run on the device at app startup
  • A plugin change can ship in an over-the-air update
  • Expo Go picks up custom plugins after a reload
  • Plugin options can be a bare object after the name
  • Editing android/ by hand is fine in a CNG project