# Development process

## Develop an app with Expo

An overview of the development process of building an Expo app to help build a mental model of the core development loop.

If you're new to Expo and React Native or you've been around the ecosystem for a while, this document will be useful to help you to better understand the development process of building an Expo app. It will help you build a mental model of the core development loop and how Expo tools fit into it.
## Key concepts
The following concepts are valuable to understand, and we recommend referring back to these definitions as you read through the rest of this guide and also as you use Expo tools.
<Collapsible summary={`What is an "Expo app"?`}>
This is a shorthand term we use for describing a _React Native app that uses Expo tools_. An "Expo app" can use a single package from the Expo SDK, or Expo Router, or Expo CLI, or Continuous Native Generation, a combination of them, or any other Expo tools.
We say "Expo app" because _React Native app that uses Expo tools_ is incredibly inconvenient to frequently type and speak out loud.
<Collapsible summary={<>Is the development process different for an "Expo app" and a "React Native app that does <span><i>not</i></span> use Expo tools"?</>}>
Expo provides a variety of tools and services that can be adopted independently, so the answer depends on which tools you choose to use. For most of what Expo provides, there is no React Native tooling provided by Meta that is comparable.
---
---
<Collapsible summary={`What is the difference between "Expo" and "Expo Application Services (EAS)"?`}>
Expo is an open-source project that gives developers powerful tools to assist in building and maintaining React Native apps at any scale. For example, Expo CLI, Expo Router, and Expo SDK packages. All Expo open-source tools are entirely free to use and carry the MIT license.
Expo Application Services (EAS) is a suite of hosted services that you can use with Expo and React Native projects to:
- Build, submit, and update your app
- Set up automation around all of these processes
- Collaborate with your team
EAS solves a set of problems that require physical resources, such as application servers and CDNs for serving over-the-air updates and physical servers for running builds. EAS has a generous [free plan](https://expo.dev/pricing#get-started) that will work for many student and hobby projects.
You don't have to use GitHub to use git, but it certainly helps for many cases. The same goes for EAS and Expo.
Note: Do I have to use EAS if I use Expo open source tools?
---
Nope! Your Expo project is just a React Native app, which is just a native app. You can use Fastlane or any native build, update, and more, tools you like.
Most EAS services also allow you to run them on your own infrastructure, and we provide instructions for how you can accomplish this. For example, [self-hosting updates](/versions/latest/sdk/updates/) (rather than using EAS Update), or [running builds locally](/guides/local-app-development/) or on [your own CI](build/building-on-ci/) (rather than using our EAS Build worker fleet).
For most teams, it makes sense to use EAS rather than spending the engineering time and resources on acquiring, setting up, and maintaining the services on other infrastructure. Additionally, EAS provides deep integration between services, such as the deployments page for monitoring app version adoption, assigning updates to specific builds, and rolling those updates out incrementally &mdash; which ties back into monitoring with [EAS Insights](/eas-insights/introduction/).
---
Note: Can I use EAS if I'm not using any Expo open source tools?
---
Yes! We think EAS is a great fit for any React Native project.
---
---
Note: Expo Go: a playground for students and learners
---
[Expo Go](https://expo.dev/go) is the fastest way to get started with React Native, especially when combined with [Snack](https://snack.expo.dev/). It's great for students and learners to understand the basics.
However, **Expo Go is a limited playground and not useful for building production-grade projects**. **If you plan on deploying your app to the store, then [development builds](#development-builds) will provide a more flexible, reliable, and complete development environment.** This guide does not go into any detail about Expo Go, and this is the only section that mentions it.
---
Note: Development builds
---
A development build is a debug build of your app that contains `expo-dev-client` library. It helps you iterate as quickly as possible and provides a more flexible, reliable, and complete development environment than Expo Go. You can install any native library and configure or apply changes to a [native project](#android-and-ios-native-projects) using [app config](/workflow/configuration/) or by creating a [config plugin](/config-plugins/introduction/). You can create a development build [locally](/guides/local-app-development/#local-builds-with-expo-dev-client) or use [EAS Build](/develop/development-builds/create-a-build/) to create a build in the cloud.
---
Note: Android and iOS native projects
---
React Native apps for mobile platforms are made of two interconnected parts:
Note: 1. The app JavaScript
---
This contains your React components and most, if not all, of your application logic. It has more or less the same role as the app JavaScript on React websites.
---
Note: 2. The native projects
---
Android and Xcode projects that bundle the JavaScript app, serve as the launchpad for the JavaScript app on each platform. They also handle the rendering of native components and provide the means to access platform-specific functionalities and integrate with any installed native libraries. App configuration, such as the name (as it appears on the home screen), icon, required permissions, associated domains, supported orientations, and so on, is configured in the native project.
---
Like any mobile app, the application that is distributed to users is created by compiling ("building") the Android Studio or Xcode project.
When you initialize a new app with `npx create-expo-app`, you will not see any **android** or **ios** directories. You can [generate the native projects by running `npx expo prebuild`](/workflow/prebuild/), which will initialize the native projects and then apply the project Expo app config (**app.json/app.config.js**) to them.
If you use a cloud-based development workflow, you may never need to run prebuild or install Android Studio or Xcode on your own machine (although you may find this useful). This is explained below in the [Local and cloud-based development workflows](#cloud-based-and-local-development-workflows).
<Collapsible summary={`Why aren't the native projects created by default when you initialize a project with create-expo-app?`}>
The default behavior encourages the use of [Continuous Native Generation](/workflow/continuous-native-generation/) (CNG) to generate the native projects when needed, which can make upgrades and project maintenance significantly easier.
The following three commands result in more or less the same project:
```sh
$ npx create-expo-app MyApp && cd MyApp && npx expo prebuild
$ npx create-expo-app --template bare-minimum
$ npx @react-native-community/cli@latest init MyApp && cd MyApp && npx install-expo-modules
```
---
---
Note: Continuous Native Generation (CNG)
---
Continuous Native Generation (CNG) is a process for building an Expo app where your [native projects](#android-and-ios-native-projects) are generated on-demand from your **app.json** and **package.json**, similar to how your **node_modules** are generated from your **package.json**.
The [native project](#android-and-ios-native-projects) directories (**android** and **ios**) are automatically added to your **.gitignore** when you create a new project, and you can delete them at any time, then re-generate them from the Expo app config with `npx expo prebuild` whenever required. You might never even run prebuild on your own development machine if you use a cloud-based development workflow.
Using CNG can make upgrading to new versions of React Native much easier. It can simplify project maintenance and facilitate setting up complex features such as [App Clips](https://github.com/bndkt/react-native-app-clip), [share extensions](https://github.com/timedtext/expo-config-plugin-ios-share-extension), and [error reporting](https://github.com/getsentry/sentry-react-native). This is all made possible with [config plugins](/config-plugins/introduction/). Learn more about [CNG](/workflow/continuous-native-generation/).
Note: What if I want to edit the native project configuration in Android Studio or Xcode rather than generating the projects with prebuild?
---
CNG has proven to be helpful to many teams. However, it may not be the best fit for your project and this is a perfectly reasonable way to use Expo tools in many cases.
You can run `npx expo prebuild` in the project and then make changes directly to the **android** and **ios** directories, instead of using Expo app config. If you decide to do this, then keep in mind that you won't be able to re-generate your projects using prebuild any longer &mdash; running prebuild after making native changes directly will overwrite all of those modifications.
Note that you can use [config plugins](/config-plugins/introduction/) to modify the native project configuration without having to modify the native projects directly, and if you decide to move back to CNG at some point.
---
Note: How do I know when I need to run prebuild again?
---
If you add a new native dependency to your project or change the project configuration in Expo app config (**app.json/app.config.js**), you can run `npx expo prebuild --clean` to re-generate the native project directories.
See [Determining third-party library compatibility](/workflow/using-libraries/#determining-third-party-library-compatibility) for more information on how to determine if a new dependency requires native code changes.
---
---
Note: Cloud-based and local development workflows
---
Whether you choose cloud-based or local doesn't significantly alter your development loop. It's about how you produce and distribute your app binaries that your JavaScript code runs against. Choosing a cloud-based or local development is a choice you can make each time you run a new native build.
Compiling your app in the cloud with EAS Build is as easy as running a single command, no need to install Android Studio or Xcode. Cloud builds make it easier to share your app with other teammates or stakeholders, [among other benefits](/build/introduction/).
To compile your app locally, you will need to install Android Studio and Xcode on your machine, and then you can either run the build from those tools or use `npx expo run:[android|ios]`. This is most useful when you want to debug your app on a physical device or emulator/simulator using the native debugging tools.
Learn more about [cloud-based workflows with EAS Build](/build/introduction/) and [local development](/guides/local-app-development/).
---
## Initialize and run a project
The easiest way to [create a new project is with `create-expo-app`](/get-started/create-a-project/). After creating your project, you can immediately launch it directly in Expo Go on your physical device or in an emulator/simulator if you want to experiment or build a quick prototype.
In most cases, you will create and use a development build of your project. You will install the [`expo-dev-client`](/develop/development-builds/introduction/#what-is-expo-dev-client) library. Development builds can be created with EAS Build or locally on your machine:
## The core development loop
The core development loop described in the diagram above is a cycle of four main activities that you typically go through when developing an app.
- #### Write and run JavaScript code
  This involves creating components, writing business logic, or installing libraries from npm that don't require native
  code changes. The changes you make here are reflected in your app without needing any interaction
  with the native side of your app.
- #### Update app configuration
  This involves modifying your app's
  configuration using the app config file (**app.json** or **app.config.js**). It includes updating
  your app's name, icon, splash screen, and other properties. These changes don't all affect the
  native project directly. However, if you make changes that affect the native projects, you can use
  the [app config](/workflow/configuration/) to modify the native project configuration or create or
  use a [config plugin](/config-plugins/introduction/). See [app config
  reference](/versions/latest/config/app/) for a complete list of properties available in the app
  config file.
- #### Write native code or modify native project configuration
  This includes writing native code directly or modifying native code configuration. You either need
  access to the native code project directories to make these changes, or you can write native code
  with a [local Expo Module](/modules/get-started/#adding-a-new-module-to-an-existing-application).
- #### Install a library that requires native code modifications
  This includes that a library requires making changes to the native code project configuration. Either
  the library provides a config plugin or steps to take to update the app config. Like the previous
  activity, this also requires you to create a development build.
When creating a [development build](#development-builds), you have two options. You can create a cloud-based build using [EAS Build](/build/setup/) or do it locally. If you choose to do it locally, you can use [CNG](#continuous-native-generation-cng) and then [`npx expo prebuild --clean`](#how-do-i-know-when-i-need), or you can create a development build using [`npx expo run android|ios` or Android Studio and Xcode](/guides/local-app-development/#local-app-compilation).
> **Note**: When creating a development build locally, the `npx expo run` commands will generate native directories before building your app. If you modify your project's configuration or native code after the first build, you will have to rebuild your project. Running `npx expo prebuild` again layers the changes on the top of existing files. It may also produce different results after the build. To avoid this, add native directories to the project's **.gitignore** and use `npx expo prebuild --clean` command.
During your app's development loop, you can also [install different variants (development, preview or production)](/build-reference/variants/) of your app on the same device.
Another key part of the development loop is debugging. See [Debugging runtime issues](/debugging/runtime-issues/) for more information about debugging your app and learn about different [debugging tools](/debugging/tools/) available.
## Share app with testers
The next step to developing an app is to share your app with your team, with beta testers, or run it on multiple test devices. A traditional approach is to upload your app's binary to the Google Play Beta (Android) or TestFlight (iOS). This can be a time-consuming effort and is limited to only one active build at a time (for example, in the case of TestFlight).
If you are using EAS Build, we recommend going through [Internal distribution](/build/internal-distribution/) to learn more about sharing your app for testing.
If you compile your app locally, you can create [production builds locally](/guides/local-app-production/).
## Release app to stores
To release your app on the app stores, you can use [EAS Submit](/submit/introduction/). For more information on using EAS Submit, see [Submit to Google Play Store](/submit/android/) and [Submit to Apple App Store](/submit/ios/).
To create a production build locally, see the [guide](/guides/local-app-production/) on the same and then go through the app stores guide to submit your app.
## Monitor app in production
Two ways to monitor your production app are crash reports and analytics. Crash reports help you learn about exceptions or errors that your users run into while using your app. You can use [Sentry](/guides/using-sentry/) or [BugSnag](https://docs.bugsnag.com/platforms/react-native/expo/) to enable crash reports.
Analytics allows you to track how users interact with your app. See [analytics overview](/guides/using-analytics/) to learn more about the services available in the Expo and React Native ecosystem.
## Update the app
The `expo-updates` library allows you to programmatically make instant updates to your app's JavaScript available to your production app.
You can use [EAS Update](/eas-update/introduction/) which provides first-class support for instant updates in a React Native app. It serves updates from the edge of a global CDN and uses modern networking protocols such as HTTP/3 for clients that support them. It is also [tailored for developers](/eas-update/develop-faster/) who use EAS Build. You can also use it for builds you have created [locally](/eas-update/standalone-service/).


## Configure with app config

Learn about what app.json/app.config.js/app.config.ts files are and how you can customize and use them dynamically.

<PossibleRedirectNotification newUrl="/versions/latest/config/app/" />
The app config (**app.json**, **app.config.js**, **app.config.ts**) is used for configuring [Expo Prebuild](/workflow/prebuild) generation, how a project loads in [Expo Go](/get-started/expo-go/), and the OTA update manifest.
It must be located at the root of your project, next to the **package.json**. Here is a minimal example:
```json app.json
{
  "name": "My app",
  "slug": "my-app"
}
```
If your Expo config has a top-level `expo: {}` object, then this will be used in place of the root object and all other keys will be ignored.
## Properties
The app config configures many things such as app name, icon, splash screen, deep linking scheme, API keys to use for some services and so on. For a complete list of available properties, see [app.json/app.config.js/app.config.ts reference](/versions/latest/config/app/).
> **info** Do you use Visual Studio Code? If so, we recommend that you install the [Expo Tools](https://marketplace.visualstudio.com/items?itemName=expo.vscode-expo-tools) extension to get auto-completion of properties in **app.json** files.
## Reading configuration values in your app
Most configuration in the app config is accessible at runtime from your JavaScript code, using [`Constants.expoConfig`](/versions/latest/sdk/constants/#nativeconstants--properties). You **should not** include any sensitive information in the app config (with a few exceptions for fields that are filtered out, as outlined below).
You can verify which configuration will be embedded in your builds/updates and available at runtime by running `npx expo config --type public`.
Note: Which fields are filtered out of the public app config?
---
The following fields are filtered out of the public app config (and not accessible through the `Constants.expoConfig` object):
- [`hooks`](/versions/latest/config/app/#hooks)
- [`ios.config`](/versions/latest/config/app/#config)
- [`android.config`](/versions/latest/config/app/#config-1)
- [`updates.codeSigningCertificate`](/versions/latest/config/app/#codesigningcertificate)
- [`updates.codeSigningMetadata`](/versions/latest/config/app/#codesigningmetadata)
---
> **warning** You should also avoid importing **app.json** or **app.config.js** directly in your JavaScript code, because this will import the entire file rather than a processed version of it. Instead, use [`Constants.expoConfig`](/versions/latest/sdk/constants/#nativeconstants--properties) to access the configuration.
## Extending configuration
Library authors can extend the app config by using [Expo Config plugins](/config-plugins/introduction/).
> **info** Config plugins are mostly used to configure the [`npx expo prebuild`](/workflow/prebuild) command.
## Dynamic configuration
For more customization, you can use the JavaScript (**app.config.js**) or [TypeScript](#using-typescript-for-configuration-appconfigts-instead-of-appconfigjs) (**app.config.ts**). These configs have the following properties:
- Comments, variables, and single quotes.
- ESM import syntax (the `import` keyword) is not supported, except when using [TypeScript with `tsx`](/guides/typescript/#appconfigjs). JS files that are compatible with your current version of Node.js can be imported with `require()`.
- TypeScript support with nullish coalescing and optional chaining.
- Updated whenever Metro bundler reloads.
- Provide environment information to your app.
- Does not support Promises.
For example, you can export an object to define your custom config:
```js app.config.js
const myValue = 'My App';
module.exports = {
  name: myValue,
  version: process.env.MY_CUSTOM_PROJECT_VERSION || '1.0.0',
  // All values in extra will be passed to your app.
  extra: {
    fact: 'kittens are cool',
  },
};
```
The `"extra"` key allows passing arbitrary configuration data to your app. The value of this key is accessed using [`expo-constants`](/versions/latest/sdk/constants/):
```js App.js
import Constants from 'expo-constants';
Constants.expoConfig.extra.fact === 'kittens are cool';
```
You can access and modify incoming config values by exporting a function that returns an object. This is useful if your project also has an **app.json**. By default, Expo CLI will read the **app.json** first and send the normalized results to the **app.config.js**.
For example, your **app.json** could look like this:
```json app.json
{
  "name": "My App"
}
```
And in your **app.config.js**, you are provided with that configuration in the arguments to the exported function:
```js app.config.js
module.exports = ({ config }) => {
  console.log(config.name); // prints 'My App'
  return {
    ...config,
  };
};
```
### Switching configuration based on the environment
It's common to have some different configuration in development, staging, and production environments, or to swap out configuration entirely to white label an app. To accomplish this, you can use **app.config.js** along with environment variables.
```js app.config.js
module.exports = () => {
  if (process.env.MY_ENVIRONMENT === 'production') {
    return {
      /* your production config */
    };
  } else {
    return {
      /* your development config */
    };
  }
};
```
To use this configuration with Expo CLI commands, set the environment variable either for specific commands or in your shell profile. To set environment variables for specific commands, prefix the command with the variables and values as shown in the example:
```sh
$ MY_ENVIRONMENT=production eas update
```
This is not anything unique to Expo CLI. On Windows you can approximate the above command with:
```sh
$ npx cross-env MY_ENVIRONMENT=production eas update
```
Or you can use any other mechanism that you are comfortable with for environment variables.
### Using TypeScript for configuration: app.config.ts instead of app.config.js
You can use autocomplete and doc-blocks with an Expo config in TypeScript. Create an **app.config.ts** with the following contents:
```ts app.config.ts
import { ExpoConfig, ConfigContext } from 'expo/config';
export default ({ config }: ConfigContext): ExpoConfig => ({
  ...config,
  slug: 'my-app',
  name: 'My App',
});
```
To import other TypeScript files into **app.config.ts** or customize the language features, we recommend using [`tsx`](/guides/typescript/#appconfigjs). `tsx` also enables using `import` syntax in any file imported by **app.config.ts**. This means you can write local [config plugins](/config-plugins/introduction/) in TypeScript with full language features.
### Configuration resolution rules
There are two different types of configs: static (**app.config.json**, **app.json**), and dynamic (**app.config.js**, **app.config.ts**). Static configs can be automatically updated with CLI tools, whereas dynamic configs must be manually updated by the developer.
1. The static config is read if **app.config.json** exists (falls back to **app.json**). If no static config exists, then default values are inferred from the **package.json** and your dependencies.
2. The dynamic config is read if either **app.config.ts** or **app.config.js** exist. If both exist, then the TypeScript config is used.
3. If the dynamic config returns a function, then the static config is passed to the function with `({ config }) => ({})`. This function can then mutate the static config values. Think of this like middleware for the static config.
4. The return value from the dynamic config is used as the final config. It cannot have any promises.
5. All functions in the config are evaluated and serialized before any tool in the Expo ecosystem uses it. The config must be a JSON manifest when it is hosted.
6. If the final config object has a top-level `expo: {}` object, then this will be used in place of the root object and all other keys will be ignored.
Running `npx expo config` will display the final configuration that will be used in Expo CLI after resolution has occurred.


## Continuous Native Generation (CNG)

Learn about managing your native projects with Continuous Native Generation (CNG) and Prebuild.

A single native project on its own is complicated to maintain, scale, and update. In a cross-platform app, you have multiple native projects that you must maintain and keep up to date with the latest operating system releases to avoid falling too far behind in any third-party dependencies.
As your native project grows, the complexity from third-party dependencies increases, complicating upgrades and slowing down developer momentum. This discourages adding advanced native functionality and leads to less powerful apps. In cross-platform apps, this complexity is multiplied across each platform.
To address this, we've introduced the concept of **Continuous Native Generation**. Instead of creating native projects a single time and maintaining customizations to those native projects for the lifetime of the codebase, short-lived native projects are generated only when needed, such as when debugging or building. These projects are generated from a standard template plus configuration or custom code that defines how the template should be customized. The result is a native project that can be compiled into a native app with any customizations desired by the developer. However, the developer is responsible for only maintaining the definition of their customizations, rather than all of the native project code.
## CNG in React Native apps
React Native apps can use **CNG** by using [Prebuild](#usage) to automate upgrades, install or uninstall libraries, apply white label customizations, share configuration across multiple apps, reduce [orphaned code](#how-does-prebuild-help-with-orphaned-code), and more.
**Expo as a framework** enables CNG by combining the following tools:
1. The [app config](/workflow/configuration/) file.
2. Arguments passed to the `npx expo prebuild` command.
3. Version of `expo` that's installed in the project and corresponding [prebuild template](https://github.com/expo/expo/tree/main/templates/expo-template-bare-minimum).
4. [Autolinking](/more/glossary-of-terms#autolinking), for linking [native modules](/more/glossary-of-terms/#native-module) found in the **package.json**.
5. Native subscribers, for reducing native code side-effects in entry point files, such as **MainApplication** or **AppDelegate**.
6. EAS Credentials for code signing additional targets and entitlements.
The end result is a workflow where a developer can express any native application with the app config and generate that project continuously &mdash; by running `npx expo prebuild`.
## Usage
Prebuild can be used by running:
```sh
$ npx expo prebuild
```
This creates the **android** and **ios** directories for running your React code. If you modify the generated directories manually then you risk losing your changes the next time you run `npx expo prebuild --clean`. Instead, use [config plugins](/config-plugins/introduction/) , which are functions that perform modifications on native projects during prebuild.
We highly recommend using Prebuild for the reasons listed in the [common questions](#prebuild) section, but the system is [fully optional](#optionality), and you can stop using it at any time.
### Usage with EAS Build
If your project does not contain **android** and **ios** directories, EAS Build will run Prebuild to generate these native directories before compilation. This is the default behavior for any project created using `npx create-expo-app`.
For a project that has **android** and **ios** directories, EAS Build will not run Prebuild to avoid overwriting any changes you've made to the native directories.
If you troubleshoot your app by [compiling it locally](/guides/local-app-development/#local-app-compilation) (running `npx expo prebuild`, or `npx expo run:android` or `npx expo run:ios`), you can still use Prebuild with EAS Build to generate fresh native directories during the build process. The **android** and **ios** directories are automatically added to **.gitignore** when you create a new project, but if you need to add them manually, you can add them to **.gitignore** or [**.easignore**](/build-reference/easignore/) files:
```diff
diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/android
+/ios
```
### Usage with Expo CLI run commands
You can perform a native build locally by running:
```sh
$ npx expo run:android
$ npx expo run:ios
```
If native directories are absent, `npx expo prebuild` will run once for the specific platform. On subsequent uses of these `run` commands, manually run `npx expo prebuild --clean` to ensure the native code is freshly synchronized with your local configuration.
## Platform support
Prebuild currently supports Android and iOS. Web support is not required because there is no native project to generate for the web and the web app it runs in a web browser. Use the `--platform` option to run prebuild for individual platforms:
```sh
$ npx expo prebuild --platform ios
```
## Dependencies
Prebuild begins by initializing new native projects from a template corresponding to each Expo SDK version. This also aligns with specific React and React Native versions. You will see a warning when running `npx expo prebuild` when your project's React and React Native versions differ from the expected versions from specified in the `dependencies` field of the [template's **package.json**](https://github.com/expo/expo/tree/main/templates/expo-template-bare-minimum) if they differ.
You can skip changing npm package versions with the `--skip-dependency-update` option:
```sh
$ npx expo prebuild --skip-dependency-update react-native
react
```
## Package managers
When the [dependencies](#dependencies) are changed, Prebuild will reinstall libraries using the package manager that is currently used in the project (this is inferred from the lockfile). You can force a specific package manager by providing one of: `--npm`, `--yarn`, `--pnpm`.
All installations can be skipped by passing the `--no-install` command, which is useful for testing generation quickly.
## Clean
The `--clean` option deletes any existing native directories before generating. Re-running `npx expo prebuild` without the `--clean` option will layer changes on top of the existing files, which is faster, but may not produce the same results in some cases.
For example, some config plugins aren't idempotent. When a project utilizes multiple "dangerous modifiers" to add regex changes to an app's code, it can lead to unexpected behavior. This is why using the `--clean` option is the safest way to use the prebuild command and is generally recommended in most cases.
#### Using `--clean` option
When using the `--clean` option, you'll be warned if you have any uncommitted changes to your git code repository, as this option will delete and recreate all of your native project files. This prompt is optional and will be skipped when encountered in CI. You can disable this check by enabling the environment variable `EXPO_NO_GIT_STATUS=1`.
There are cases where developers may want to swap between workflows often. For example, you may want to build custom functionality natively in Android Studio and Xcode, and then move that functionality into local config plugins.
## Templates
You can customize how the native directories are generated by [config plugins](/config-plugins/introduction). Many config plugins already exist for lots of modifications, and community libraries often ship their own as well. You can [see a list of some popular plugins](https://github.com/expo/config-plugins) for more information.
Prebuild starts from template files, which are then modified with config plugins. The template files are based on the Expo SDK version and come from the npm package [`expo-template-bare-minimum`](https://github.com/expo/expo/tree/main/templates/expo-template-bare-minimum). You can change the template used by passing `--template /path/to/template.tgz` to the `npx expo prebuild` command. This is not generally recommended because the base modifiers in `@expo/prebuild-config` make some undocumented assumptions about the template files, so it may be tricky to maintain your custom template.
> **Note:** In network environments where all packages are downloaded from a private registry and npm public registry access is blocked, a locally-available template must be passed to the prebuild command. [Learn more about using a local version of the default template](https://expo.fyi/prebuild-without-npm-access).
## Side effects
`npx expo prebuild` performs several side effects outside of generating the **android** and **ios** directories. Work is in progress to eliminate these side effects &mdash; ideally, running `npx expo prebuild` would generate the Android and iOS projects and leave the rest of the project untouched.
In addition to generating the native directories, prebuild also makes the following modifications:
- Modifies the `scripts` field in the **package.json** to replace `expo start --android` and `expo start --ios` with `expo run:android` and `expo run:ios`
- Modifies the `dependencies` field in the **package.json**
The convenience change to the `scripts` field is the only side effect that alters how a developer works on their app before/after prebuild. All other changes can be left in place and committed to git to minimize the diff when running prebuild.
## Optionality
Prebuild is optional and works seamlessly with all Expo tools and services. For existing React Native projects, where the native projects are managed manually, do not use `npx expo prebuild`, as that may overwrite any manual customizations. Developers can continue to make ([direct changes to their native projects](/more/glossary-of-terms/#bare-workflow)) while adopting other Expo tools and workflows. Later on, they can move their manual customizations to app config and/or config plugins, and then adopt CNG.
Everything offered by Expo including [EAS](/eas/), Expo CLI, and the libraries in the Expo SDK are built to **fully support** bare React Native projects as this is a minimum requirement for supporting projects using `npx expo prebuild`. The only exception is the [Expo Go](https://expo.dev/go) app, which can load arbitrary React Native projects only if they include JavaScript fallbacks for native code absent in the Expo Go runtime.
## Common questions
### CNG
Note: How does CNG help with project upgrades?
---
React Native developers who don't use **Continuous Native Generation** have reported that upgrading their apps to the latest version of React Native is the number one weakness of the library as per [React Native Survey (2022)](https://results.2022.stateofreactnative.com/opinions/#opinions_pain_points).
When using CNG, the upgrade process simply involves upgrading the npm dependencies, app config, and re-running `npx expo prebuild --clean`.
---
Note: How a React Native library author can adopt CNG?
---
React Native library authors can adopt CNG in several ways. It depends on the complexity of their libraries. Here are a few scenarios:
- **No native code or configuration side-effects**: Libraries without native code or configuration side-effects, such as `react-native-blurhash`, can seamlessly integrate with `npx expo prebuild`. They can rely on Node Module Resolution without requiring any additional configuration.
- **Native code with no additional setup after install**: Libraries with native code can often be installed and linked automatically with [Expo Autolinking](/more/glossary-of-terms#autolinking), which runs before the native app is built.
- **Additional configuration side-effects and setup**: Libraries that require additional configuration side-effects can adopt CNG by creating [Expo config plugins](/config-plugins/introduction/) for their libraries. This approach enables library authors to automate adding values such as permission messages to the **Info.plist**, or injecting targets in the Xcode project.
- **Libraries Dependent on Native Runtime Hooks**: Libraries that depend on specific native runtime hooks, such as intercepting the initial launch URL via the `AppDelegate`, `MainActivity`, `MainApplication`, and so on, can utilize [**Lifecycle listeners**](/modules/android-lifecycle-listeners/) in the Expo Modules API. These lifecycle listeners allow these runtime hooks to be applied via Expo Autolinking instead of by modifying these standard native project files, eliminating the need for a config plugin.
Many complex libraries and services already support CNG via Expo Prebuild such as, [MapBox](https://github.com/rnmapbox/maps), [Sentry](https://github.com/getsentry/sentry-react-native), [Stripe](https://github.com/stripe/stripe-react-native), and [React Native Firebase](https://rnfirebase.io/#expo).
Adopting CNG by library authors is not a preqrequisite for using `npx expo prebuild`. If a library author has not adopted CNG, developers can still use `npx expo prebuild` by creating local [Config Plugins](https://github.com/expo/config-plugins/) to modify the native generation pipeline. This flexibility makes CNG accessible and beneficial to all developers within the React Native community.
---
Note: Is CNG limited to React Native projects?
---
No, CNG is a versatile pattern that can be applied to any native project. While Expo Prebuild is a tool that implements CNG specifically for React Native projects, the concept itself is not limited to this framework.
---
Note: How does community use CNG?
---
Here are a few community examples of difficult native features converted into simple configuration files, which have allowed developers to build more powerful apps without compromising on iteration speed:
- [iOS Safari Extensions](https://github.com/andrew-levy/react-native-safari-extension): Here, the process of creating a Safari Extension for iOS, which is a notoriously difficult feature to implement, is reduced to a few of lines of JSON.
- [iMessage Sticker App](https://github.com/expo/config-plugins/tree/main/packages/ios-stickers): This Expo config plugin can generate an entire iMessage Sticker App from a JSON object.
- [Cross-platform end-to-end testing](https://github.com/expo/config-plugins/tree/main/packages/detox): Configure native apps to support E2E testing with Detox in a single-line.
- [The entire Firebase suite](https://rnfirebase.io/): Here you can see the entire native Firebase suite going from a multi-step native configuration process across multiple IDEs, down to basic JSON configuration.
- [Cross-platform home screen widgets](https://github.com/gaishimo/eas-widget-example): This Expo config plugin can generate a home screen widget for Android and iOS.
- [Apple App Clips](https://github.com/bndkt/react-native-app-clip): This Expo config plugin takes the process of generating an Apple App Clip from a multi-step process, ranging across multiple targets, and reduces it to a single line `["react-native-app-clip", { "name": "My App Clip" }]`.
At any point, these features can be easily added and removed, without any side effects. CNG allows developers to experiment with complex features and iterate on them quickly without worrying about the long-term maintenance costs or potential orphaned code in their project.
---
Note: Can CNG be used for operating systems other than Android and iOS?
---
Absolutely! CNG is an abstract concept that can be applied to any operating system. Although Expo Prebuild officially implements CNG for Android and iOS, it also provides abstract platform support for developers to create implementations for additional platforms.
---
Note: Is using Expo a requirement for CNG?
---
Not at all. CNG is an open pattern that can be adopted by any community. We've defined the pattern abstractly to help other communities understand how they can adopt CNG for their own projects.
---
Note: How does CNG compare to web development patterns such as Static Site Generation (SSG)?
---
CNG shares similarities with SSG in that it generates a project from a set of inputs. However, CNG differs from SSG in its output. It generates native runtime code instead of static website code. This means the native project is generated on-demand, and the generated source code and configuration are discarded once the native project is compiled into a native app.
---
Note: Is it possible to use CNG with an existing brownfield project?
---
CNG is designed to manage the entire state of a native project continuously. As a result, it's not intended for use with existing brownfield projects. However, you can use CNG to generate a new native project, which can then be integrated into an existing brownfield project.
---
### Prebuild
Expo Prebuild streamlines CNG processing. Here are some issues in the React Native development cycle that are addressed by Prebuild:
Note: How can Prebuild help with sensible project upgrades?
---
Building native code requires familiarity with the platform's tooling, creating a steep learning curve. This challenge intensifies in cross-platform development due to multiple platforms. Cross-platform tooling doesn't help if you must implement many features in platform-specific native code.
When bootstrapping a native app, there's initial code and configuration that you may not understand. Yet you are not responsible for maintaining it. Eventually, you'll need to understand this code to upgrade your app safely. This challenge often leads developers to upgrade incorrectly or start a new app, copying existing source code.
**With Prebuild**, upgrading is much closer to upgrading a pure JavaScript application. Bump the versions in your **package.json**, regenerate the native project, and you should be ready to continue development.
---
Note: How does Prebuild simplify cross-platform configuration?
---
Cross-platform configurations such as the app icon, name, splash screen, and so on must be implemented manually in native code. These implementations are often quite different for each platform.
**With Prebuild** cross-platform configurations are handled at the config plugin level, and the developer only needs to set a single value like `"icon": "./icon.png"` to have all icon generation taken care of.
---
Note: How can I manage dependency side-effects with Prebuild?
---
Many complex native packages require additional setup beyond installing and [autolinking](/more/glossary-of-terms#autolinking). For example, a camera library requires permission settings to be added **AndroidManifest.xml** for Android and **Info.plist** for iOS. This additional setup can be considered a configuration side effect of a package. Pasting the required side effect code into your project's native files can lead to difficult native compilation errors, and it's also code that you now own and maintain.
**With Prebuild** library authors, who know how to configure their library better than anyone, can create a testable and versioned script called a [config plugin](/config-plugins/introduction/), to automate adding the required configuration side effects for their library. This means library side effects can be more expressive, powerful, and stable. For native code side effects, we also provide [Android Lifecycle Listeners](/modules/android-lifecycle-listeners) and [AppDelegate Subscribers](/modules/appdelegate-subscribers) which come standard in the default [prebuild template](#templates).
---
Note: How does Prebuild help with orphaned code?
---
When you uninstall a package, you have to be certain you removed all of the side effects required to make that package work. If you miss anything, it leads to orphaned code that you cannot trace back to any particular package, this code builds up and makes your project harder to understand and maintain.
**With Prebuild** the only side effect is the [config plugin](/config-plugins/introduction/) in a project's Expo config (**app.json**), which will throw an error when the corresponding node module has been uninstalled, meaning a lot less orphaned configuration.
---
Note: When Prebuild might not be the right fit for a project
---
Here are some reasons Expo Prebuild might **not** be the right fit for a particular project:
#### Platform compatibility
Prebuild can only be used for native platforms that are supported by the Expo SDK. This means Android and iOS for the time being. Except for web, which doesn't require `npx expo prebuild` since it uses the browser instead of a custom native runtime.
#### Making changes directly is quicker than modularizing and automating
All native changes must be added with native modules (using React Native's built-in Native Module APIs or the Expo Modules API) and config plugins. This means if you want to quickly add a native file to your project to experiment, then you may be better off running prebuild and adding the file manually, then working your way back into the system with a [monorepo](/guides/monorepos). We plan to speed this process up by adding functionality to [Expo Autolinking](/more/glossary-of-terms#expo-autolinking) that finds native project files outside of the native directories and links them before building.
If you want to modify the configuration, such as the **gradle.properties** file, you'll have to write a plugin ([example](https://github.com/expo/expo/blob/1c994bb042ad47fbf6878e3b5793d4545f2d1208/apps/native-component-list/app.config.js#L21-L28)). This can be easily automated with helper plugin libraries, however, it is a bit slower if you need to do it often.
#### Config plugin support in the community
Not all packages support _Expo Prebuild_ yet. If you find a library that requires extra setup after installation and doesn't yet have a config plugin, we recommend opening a pull request or an issue so that the maintainer is aware of the feature request.
Many packages, such as [`react-native-blurhash`](https://github.com/mrousavy/react-native-blurhash), don't require any additional native configuration beyond what is handled by [autolinking](/more/glossary-of-terms#autolinking) and so no config plugin is required.
Other packages, such as [`react-native-ble-plx`](https://github.com/Polidea/react-native-ble-plx), do require additional setup and therefore require a config plugin to be used with `npx expo prebuild` (in this case there's an external plugin called [`@config-plugins/react-native-ble-plx`](https://github.com/expo/config-plugins/tree/main/packages/react-native-ble-plx)).
Alternatively, we also have a repo for [out-of-tree config plugins](https://github.com/expo/config-plugins) which provides plugins for popular packages that haven't adopted the system yet. Think of this like [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) for TypeScript. We prefer packages ship their own config plugin, but if they haven't adopted the system yet, the community can use the packages listed in the repo.
---


## Using Expo SDK, React Native, and third-party libraries

Learn how to use Expo SDK, React Native libraries, and other third-party npm packages in your project.

Every app is inevitably going to use a third-party library, so it's important to understand how to determine whether a library is compatible with your project.
## React Native core libraries
React Native provides a set of built-in primitives that most developers will need in their apps. These include components such as `<ActivityIndicator>`, `<TextInput>`, `<Text>`, `<ScrollView>`, and `<View>`. These are listed at [Core Components and APIs](https://reactnative.dev/docs/components-and-apis) in React Native documentation. You can also view the [React Native version that corresponds to your Expo SDK version](/versions/latest).
To use a React Native component or API in your project, import it from the `react-native` package in your code:
<SnackInline>
```tsx
import { Text, View } from 'react-native';
export default function App() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Hello, world!</Text>
    </View>
  );
}
```
## Expo SDK libraries
The Expo SDK picks up where the React Native core libraries end. It provides access to a lot of device and system functionality such as audio, barcode scanning, camera, calendar, contacts, video, and so on. It also adds other powerful libraries like updates, maps, OAuth authentication tools, and more. For more information, see how we decide [what goes into the Expo SDK](https://expo.fyi/whats-in-the-sdk).
To use a library from the Expo SDK, find the one you are looking for in the [API reference](/versions/latest/) or use the documentation search.
<ConfigReactNative>
If you initialized your app using `npx @react-native-community/cli@latest init` and you don't have the `expo` package installed in it yet, see the [installing Expo modules guide](/bare/installing-expo-modules) for more information.
</ConfigReactNative>
You will see the platform compatibility tags at the top of each API reference. It tells you which platforms and environments the library is compatible with. For example, the platform tags for the [`expo-device`](/versions/latest/sdk/device/) library look like the following:
After the platform compatibility table, there is a library's description and a section with instructions on installing the library. For example:
<InstallSection packageName="expo-device" hideBareInstructions />
The `npx expo install` command picks a library version compatible with your project and then uses your JavaScript package manager (such as npm) to install it.
Next, a typical API reference includes:
- Config plugin usage information if the library requires a config plugin.
- A code example that shows how to use the library.
- API section that lists how to import the library, followed by a list of hooks, props, types, methods, and classes that the library provides.
> **Note**: If you use TypeScript, you can see the information included in the API section in your TypeScript-compatible code editor (such as VS Code) with auto-completion.
## Third-party libraries
### Finding a third-party library
[React Native Directory](https://reactnative.directory) is a searchable database of libraries built specifically for React Native. If the library that you are looking for is not provided by React Native or the Expo SDK then this is the best place to look first when trying to find a library for your app.
After the React Native Directory, the [npm registry](https://www.npmjs.com/) is the next best place. The npm registry is the definitive source for JavaScript libraries, but the libraries that it lists may not all be compatible with React Native. React Native is one of many JavaScript programming environments, including Node.js, web browsers, Electron, and more, and npm includes libraries that work for all of these environments. Any library that is compatible with React Native is compatible with the Expo project when you create a [development build](/workflow/overview/#development-builds). However, it may not be compatible with the [Expo Go](https://expo.dev/go) app.
### Determining third-party library compatibility
Use Expo [development builds](/workflow/overview/#development-builds) for building production-quality apps. It includes all of the native code that your project needs to run. This is a great way to test your app before you publish it to the App Store or Google Play. You can also include libraries that require native projects (**android** and **ios** directories) configuration.
Expo Go is a playground for students and learners to quickly try out React Native. It does not include all of the native code required to support every library, so it's limited and not useful for building production-grade projects. You can check **React Native Directory** to find a library compatible with Expo Go by visiting the website and verifying that it has a "âï¸ Expo Go" tag. You can also enable the [filter by Expo Go](https://reactnative.directory/?expoGo=true).
To determine if a new dependency changes native project directories, you can check the following:
- Does the library includes **android** or **ios** directories?
- Does the library's README mention linking?
- Does the library requires you to change **android/app/src/main/AndroidManifest.xml** or **ios/Podfile** or **ios/Info.plist** to change the project configuration?
- Does the library have a [config plugin](/config-plugins/introduction/)?
**If you answered yes to any of these questions,** then you should [create a development build](/develop/development-builds/introduction/) to use the library in your project.
**Not listed on the directory?** You can find the project on GitHub. A simple way to do this is with `npx npm-home --github <package-name>`. For example, to open the GitHub page for `react-native-localize`, run:
```sh
$ npx npm-home --github react-native-localize
```
> If you want some help determining library compatibility, [create an issue on the React Native Directory repository](https://github.com/react-native-community/directory/issues/new/choose) and let us know. This will not just help you, it will also help other developers have an easy answer in the future!
### Installing a third-party library
> We recommend always using `npx expo install` instead of `npm install` or `yarn add` directly because it allows [Expo CLI](/more/expo-cli/) to pick a compatible version of a library when possible and also warn you about known incompatibilities.
Once you have determined if the library is compatible with React Native, use [Expo CLI](/more/expo-cli/) to install the package:
```sh
$ npx expo install @react-navigation/native
```
Be sure to follow the project website or README for any additional configuration and usage instructions. You can get to the README quickly using this command:
```sh
$ npx npm-home @react-navigation/native
```
If the module needs additional native configuration, you can do so using [config plugins](/config-plugins/introduction/). Some packages require a config plugin but they don't have one yet, you can refer to the list of [out-of-tree config plugins](https://github.com/expo/config-plugins/).
<ConfigReactNative>
If your project does not support [Expo Prebuild](/workflow/prebuild) then you won't be able to use [config plugins](/config-plugins/introduction/). You can either [adopt Expo Prebuild](/guides/adopting-prebuild) or set up and configure each library manually by following any additional setup guides from the respective module's website or README.
</ConfigReactNative>
If the module is not supported in [Expo Go](https://expo.dev/go), you can create a [development build](/develop/development-builds/introduction/):
### Excluding a third-party library from version checks
If you have a specific version of a third-party library installed and want it to be excluded from version checks performed by `npx expo install`, `npx expo-doctor`, or `npx expo start`, use the [`expo.install.exclude`](/versions/latest/config/package-json/#exclude) property in the **package.json** file.


## Privacy manifests

Learn about configuring iOS privacy manifests for your mobile app.

If you're using a native iOS library that uses a "restricted reason" APIs, you'll need to configure an iOS privacy manifest to declare why you're including native code to call those APIs.
More details and a list of "required reason" APIs can be found in the [Apple Developer Documentation](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files).
> **info** The information and steps included in this guide are still in development and may change due to new tools built for this purpose or new requirements from Apple.
## What is a Privacy manifest?
A privacy manifest is a file named **PrivacyInfo.xcprivacy** that is included in your iOS native project. This file is used to declare why the app includes native code that calls into certain APIs that Apple considers sensitive.
These APIs currently include accessing UserDefaults, file timestamp, system boot time, disk space, and active keyboard. Apple considers it an open list that can be expanded in the future.
## Configuration in app config
You can include an iOS privacy manifest by using the `privacyManifests` field under `expo.ios` in your app config.
```json app.json
{
  "expo": {
    "name": "My App",
    "slug": "my-app",
    "ios": {
      "privacyManifests": {
        "NSPrivacyAccessedAPITypes": [
          {
            "NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryUserDefaults",
            "NSPrivacyAccessedAPITypeReasons": ["CA92.1"]
          }
        ]
      }
    }
  }
}
```
Make sure you have updated your Expo SDK libraries to the latest versions for your SDK version using `npx expo install --fix`.
<ConfigReactNative>
You can include an iOS privacy manifest in a bare Expo app by creating a **PrivacyInfo.xcprivacy** file using Xcode and adding it to your iOS app target.
Follow [Apple's Privacy manifest files](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files) guide to create a **PrivacyInfo.xcprivacy** file.
</ConfigReactNative>
You can identify the `NSPrivacyAccessedAPITypes` and `NSPrivacyAccessedAPITypeReasons` values by looking at the [Apple Developer documentation](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api).
### Including required reasons for Expo SDK packages and other third-party libraries
As of now, Apple does not correctly parse all the **PrivacyInfo** files included by static CocoaPods dependencies (such as Expo SDK packages and other ecosystem libraries). You may need to include the required reasons for the APIs used by those dependencies in your app's **PrivacyInfo.xcprivacy** file or the configuration in the **app.json**.
All Expo SDK packages that use "required reason" APIs file have a **PrivacyInfo** file included in the package directory. Here's [an example file](https://github.com/expo/expo/blob/main/packages/expo-application/ios/PrivacyInfo.xcprivacy) included with the `expo-application` library.
You can usually identify the required reasons for the APIs used by other third-party libraries by checking if the library you intend to use has a **PrivacyInfo.xcprivacy** file in the **node_modules/package_name/ios** directory. If it does, you can check the `NSPrivacyAccessedAPITypes` and `NSPrivacyAccessedAPITypeReasons` values in that file and copy those values to your configuration.
As an alternative, Apple notifies developers after they submit a build with missing privacy manifest files or specific reasons. You can wait until you receive a notification email from Apple and then include the required reasons listed in the email in your app's **PrivacyInfo.xcprivacy** file (if you don't use [CNG](/workflow/continuous-native-generation/)) or the configuration in your **app.json** file.
## Testing the Privacy manifest
You can test the privacy manifest by building your app and submitting it, either through App Store review process or to TestFlight's external review. Apple will email you within a few minutes of submitting if your app is missing any required reasons for the APIs used.


## Permissions

Learn about configuring and adding permissions in an app config file.

When developing a native app that requires access to potentially sensitive information on a user's device, such as their location or contacts, the app must request the user's permission first. For example, to access the user's media library, the app will need to run [`MediaLibrary.requestPermissionsAsync()`](/versions/latest/sdk/media-library#medialibraryrequestpermissionsasync).
Permissions in standalone and [development builds](/develop/development-builds/introduction/) require native build-time configuration before they can be requested using runtime JavaScript code. This is not required when testing projects in the [Expo Go](https://expo.dev/go) app.
> If you don't configure or explain the native permissions properly **it may result in your app getting rejected or pulled from the stores**.
## Android
Permissions are configured with the [`android.permissions`](/versions/latest/config/app/#permissions) and [`android.blockedPermissions`](/versions/latest/config/app/#blockedpermissions) keys in your [app config](/workflow/configuration/).
Most permissions are added automatically by libraries that you use in your app either with [config plugins](/config-plugins/plugins-and-mods/#create-a-plugin) or with a package-level **AndroidManifest.xml**. You only need to use `android.permissions` to add additional permissions that are not included by default in a library.
```json app.json
{
  "android": {
    "permissions": ["android.permission.SCHEDULE_EXACT_ALARM"]
  }
}
```
The only way to remove permissions that are added by package-level **AndroidManifest.xml** files is to block them with the [`android.blockedPermissions`](/versions/latest/config/app/#blockedpermissions) property. To do this, specify the **full permission name**. For example, if you want to remove the audio recording permissions added by `expo-camera`:
```json app.json
{
  "android": {
    "blockedPermissions": ["android.permission.RECORD_AUDIO"]
  }
}
```
- See [`android.permissions`](/versions/latest/config/app/#permissions) to learn about which permissions are included in the default [prebuild template](/workflow/prebuild#templates).
- Apps using _dangerous_ or _signature_ permissions without valid reasons **may be rejected by Google**. Ensure you follow the [Android permissions best practices](https://developer.android.com/training/permissions/usage-notes) when submitting your app.
- [All available Android `Manifest.permissions`](https://developer.android.com/reference/android/Manifest.permission).
<ConfigReactNative>
Modify **AndroidManifest.xml** to exclude specific permissions: add the `tools:node="remove"` attribute to a `<use-permission>` tag to ensure it is removed, even if it's included in a library's **AndroidManifest.xml**.
```xml
<manifest xmlns:tools="http://schemas.android.com/tools">
  <uses-permission tools:node="remove" android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>
```
> You have to define the `xmlns:tools` attribute on `<manifest>` before you can use the `tools:node` attribute on permissions.
</ConfigReactNative>
## iOS
Your iOS app can ask for system permissions from the user. For example, to use the device's camera or access photos, Apple requires an explanation for how your app makes use of that data. Most packages will automatically provide a boilerplate reason for a given permission with [config plugins](/config-plugins/introduction/). These default messages will most likely need to be tailored to your specific use case for your app to be accepted by the App Store.
To set permission messages, use the [`ios.infoPlist`](/versions/latest/config/app/#infoplist) key in your [app config](/workflow/configuration/), for example:
```json app.json
{
  "ios": {
    "infoPlist": {
      "NSCameraUsageDescription": "This app uses the camera to scan barcodes on event tickets."
    }
  }
}
```
Many of these properties are also directly configurable using the [config plugin](/config-plugins/introduction/) properties associated with the library that adds them. For example, with [`expo-media-library`](/versions/latest/sdk/media-library) you can configure photo permission messages like this:
```json app.json
{
  "plugins": [
    [
      "expo-media-library",
      {
        "photosPermission": "Allow $(PRODUCT_NAME) to access your photos.",
        "savePhotosPermission": "Allow $(PRODUCT_NAME) to save photos."
      }
    ]
  ]
}
```
- Changes to the **Info.plist** cannot be updated over-the-air, they will only be deployed when you submit a new native binary. For example, with [`eas build`](/build/introduction).
- Apple's official [permission message recommendations](https://developer.apple.com/design/human-interface-guidelines/privacy#Requesting-permission).
- [All available **Info.plist** properties](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html).
<ConfigReactNative>
Add and modify the permission message values in **Info.plist** file directly. We recommend doing this directly in Xcode for autocompletion.
</ConfigReactNative>
## Web
On the web, permissions like the `Camera` and `Location` can only be requested from a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#When_is_a_context_considered_secure). For example, using `https://` or `http://localhost`. This limitation is similar to Android's manifest permissions and iOS's **Info.plist** usage messages and is enforced to increase privacy.
## Resetting permissions
Often you want to be able to test what happens when a user rejects permissions, to ensure your app reacts gracefully. An operating-system level restriction on both Android and iOS prohibits an app from asking for the same permission more than once (you can imagine how this could be annoying for the user to be repeatedly prompted for permissions after rejecting them). To test different flows involving permissions in development, you may need to uninstall and reinstall the native app.
When testing in [Expo Go](https://expo.dev/go), you can delete the app and reinstall it by running `npx expo start` and pressing <kbd>i</kbd> or <kbd>a</kbd> in the [Expo CLI](/more/expo-cli/) Terminal UI.


## Environment variables in Expo

Learn how to use environment variables in an Expo project.

Environment variables are key-value pairs configured outside your source code that allow your app to behave differently depending on the environment. For example, you can enable or disable certain features when building a test version of your app, or switch to a different API endpoint when building for production.
The Expo CLI will automatically load environment variables with an `EXPO_PUBLIC_` prefix from **.env** files for use within your JavaScript code whenever you use the Expo CLI, such as when running `npx expo start` to start your app in local development mode.
## Reading environment variables from .env files
Create a **.env** file in the root of your project directory and add environment-specific variables on new lines in the form of `EXPO_PUBLIC_[NAME]=VALUE`:
```bash .env
EXPO_PUBLIC_API_URL=https://staging.example.com
EXPO_PUBLIC_API_KEY=abc123
```
Now you can use environment variables directly in your source code:
```tsx
import { Button } from 'react-native';
function Post() {
  const apiUrl = process.env.EXPO_PUBLIC_API_URL;
  async function onPress() {
    await fetch(apiUrl, { ... })
  }
  return <Button onPress={onPress} title="Post" />;
}
```
When you run `npx expo start`, `process.env.EXPO_PUBLIC_API_URL` will be replaced with `https://staging.example.com` in your app bundle. Variables can be updated as you edit your code without restarting the Expo CLI or clearing the cache. You will need to perform a full reload (for example, shake gesture and then Reload in Expo Go or your development build) to see the updated value.
> **warning** Do not store sensitive info, such as private keys, in `EXPO_PUBLIC_` variables. These variables will be visible in plain-text in your compiled application.
### How variables are loaded
Expo CLI loads **.env** files according to the [standard .env file resolution](https://github.com/bkeepers/dotenv/blob/c6e583a/README.md#what-other-env-files-can-i-use) and then replaces all references in your code to `process.env.EXPO_PUBLIC_[VARNAME]` with the corresponding value set in the **.env** files. Code inside **node_modules** is not affected for security purposes.
### How to read from environment variables
- <YesIcon small /> **Every environment variable must be statically referenced as a property of
  `process.env` using JavaScript's dot notation for it to be inlined.** For example, the expression
  `process.env.EXPO_PUBLIC_KEY` is valid and will be inlined.
- <NoIcon small /> **Alternative versions of the expression are not supported**. For example,
  `process.env['EXPO_PUBLIC_KEY']` or `const {EXPO_PUBLIC_X} = process.env` is invalid and will not
  be inlined.
### Using multiple .env files to define separate environments
You can define any of the [standard .env files](https://github.com/bkeepers/dotenv/blob/c6e583a/README.md#what-other-env-files-can-i-use), so it is possible to have separate **.env** and **.env.local**, files and they will load according to the standard priority.
You may choose to commit the default **.env** file or other standard configurations, but generally **.env.local** files should be added to your **.gitignore**, because they are used to specify environment configuration specific to your local machine (such as, for example, your network IP address if you need it to make a request against a local server).
```bash .gitignore
.env*.local
```
#### Environment variables and `NODE_ENV`
We recommend against using `NODE_ENV` to switch between **.env** files (such as **.env.test** and **.env.production**). While it is technically possible (`NODE_ENV=test npx expo start` will load **.env.test**) &mdash; it may not behave as you would expect. For example, `npx expo export` always forces `NODE_ENV` to `production`, so `NODE_ENV=test npx expo export` will not actually run the command with the `NODE_ENV` set to `test`.
Other tools that build on top of Expo CLI commands will exhibit the same behavior &mdash; for example, `eas update` calls into `npx expo export` and, as a result, `NODE_ENV=test eas update` will similarly not run with the `NODE_ENV` set to `test` (it will be `production`). The `NODE_ENV` environment variable is used by many tools in different ways (for example, if you run `NODE_ENV=production npm install` then your `devDependencies` will not be installed) and we have found that for React Native projects, it's best not to overload it further for this use case.
If you use EAS, consider using `eas env:pull` instead. This will swap your **.env.local** with an environment of your choice, rather than depending on `NODE_ENV`. You can accomplish a similar behavior without EAS by writing a script to overwrite **.env.local** or **.env** with the appropriate contents for the environment you wish to work with.
### Disabling environment variables
Environment variables in Expo CLI have two parts and both can be disabled:
1. Expo CLI automatically loads the **.env** files into the global process. To disable this behavior, set the environment variable `EXPO_NO_DOTENV` to `1` before running any Expo CLI command: `EXPO_NO_DOTENV=1`.
2. Expo's Metro config includes the inline serialization of environment variables in the client JavaScript bundle. To disable this behavior, you can use `EXPO_NO_CLIENT_ENV_VARS=1`.
If you're experiencing issues with environment variables, you can try disabling one or both of these features.
## Environment variables in Expo Application Services
### EAS Build
[EAS Build](/build/introduction/) uses Metro Bundler to build the JavaScript bundle embedded within your app binary, so it will use **.env** files uploaded with your build job to inline `EXPO_PUBLIC_` variables into your code. EAS Build also lets you define environment variables within build profiles in **eas.json** and via EAS Secrets. Check out the EAS Build documentation on [environment variables and build secrets](/build-reference/variables/) for more information.
### EAS Update
[EAS Update](/eas-update/introduction) uses Metro Bundler in your local environment or CI to build your app bundle, so it will use available **.env** files to inline `EXPO_PUBLIC_` variables into your code. Check out the EAS Update documentation on [environment variables](/eas/environment-variables/usage/#using-environment-variables-with-eas-update) for more information.
## Migrating to Expo environment variables
### From react-native-config
Update your **.env** files to prefix any variables used within your JavaScript code with `EXPO_PUBLIC_`:
```diff .env
- API_URL=https://myapi.com
+ EXPO_PUBLIC_API_URL=https://myapi.com
```
> If you have any non-standard **.env** files (for example, **.env.staging**), you will need to migrate those to one of the [standard .env files](https://github.com/bkeepers/dotenv/blob/c6e583a/README.md#what-other-env-files-can-i-use).
Then update your code to use `process.env.EXPO_PUBLIC_[VARNAME]`:
```diff
- import Config from 'react-native-config';
- const apiUrl = Config.API_URL;
+ const apiUrl = process.env.EXPO_PUBLIC_API_URL;
```
### From babel-plugin-transform-inline-environment-variables
Using a Babel plugin to transform your environment variable references in your code is similar to how Expo environment variables work. Set your variables inside a **.env** file and update your variable names to use the `EXPO_PUBLIC_` prefix:
```diff
- const apiUrl = process.env.API_URL;
+ const apiUrl = process.env.EXPO_PUBLIC_API_URL;
```
Then you can remove the plugin from your [Babel config](/versions/latest/config/babel/):
```diff babel.config.js
module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
--    plugins: ['transform-inline-environment-variables'],
  };
};
```
After updating your Babel config file, be sure to clear your cache with `npx expo start --clear`.
### From direnv
Move any environment variables used in your JavaScript from their **.envrc** file to a **.env** file and prefix it with `EXPO_PUBLIC_`.
Previously with `direnv`, you would need to use a [dynamic app config](/versions/latest/config/app/#app-config) that reads from [`process.env`](https://nodejs.org/dist/latest/docs/api/process.html#process_process_env) to set environment variables on the `extra` field so they can be used in your JavaScript code via [`expo-constants`](/versions/latest/sdk/constants). Move those references directly into your code, adding the `EXPO_PUBLIC_` prefix:
```diff
- import Constants from 'expo-constants';
- const apiUrl = Constants.expoConfig.extra.apiUrl;
+ const apiUrl = process.env.EXPO_PUBLIC_API_URL;
```
> [`direnv`](https://direnv.net/) automatically loads and unloads environment variables in your shell depending on your current directory, meaning it can affect the environment for any process running in that directly, not just the Expo CLI. You will likely want to continue using `direnv` for other environment variables that are not used in your JavaScript code.
## Security considerations
Never store sensitive secrets in environment variables that are prefixed with `EXPO_PUBLIC_`. When an end-user runs your app, they have access to all of the code and embedded environment variables in your app. Read more about [storing sensitive info](https://reactnative.dev/docs/security#storing-sensitive-info).