Build trigger patterns support wildcards
Build trigger patterns can include wildcards represented by asterisks (*) which match any character and any number of characters. For example, releases/* matches releases/, release/1234, release/genesis, etc. A single asterisk (*) matches all branches or tags.
GitHub PR build requires contributor status
To trigger builds from pull requests via build triggers, the PR author must be a collaborator on the GitHub repository. To build pull requests from external contributors, use PR labels instead (eas-build-[platform]:[profile] format).
GitHub build error comments on commits
When a GitHub-triggered build fails, Expo comments on the commit with error information to help with troubleshooting.
Build profile must exist in eas.json for GitHub builds
If a build profile specified in a GitHub build trigger or PR label cannot be found in eas.json, the build will not be dispatched. Verify that the profile name matches exactly with the profiles defined in your eas.json file.
Configuration file resolution order
Configuration resolution follows this order: (1) Static config from app.config.json or app.json if either exists, otherwise defaults from package.json; (2) Dynamic config from app.config.ts or app.config.js (TypeScript takes precedence if both exist); (3) If dynamic config is a function, static config is passed with ({ config }) => {} and can mutate the config; (4) Return value from dynamic config is used as final config; (5) Config is evaluated and serialized before use; (6) If final config has top-level expo: {} object, only that is used.
App config purpose and usage
The app config is used for configuring Expo Prebuild generation, how a project loads in Expo Go, and the OTA update manifest. Most configuration is accessible at runtime using Constants.expoConfig from expo-constants.
Dynamic configuration with app.config.js
app.config.js supports comments, variables, single quotes, ESM import syntax, and can export either an object or a function. The function receives ({ config }) as argument where config is the normalized result from app.json. The config is updated whenever Metro bundler reloads. Promises are not supported.
Dynamic configuration with app.config.ts
app.config.ts provides TypeScript support with nullish coalescing and optional chaining. It must export a function that returns an ExpoConfig object. To import other TypeScript files or customize language features, use the tsx tool.
Environment-based configuration switching
Use app.config.js to switch configuration based on environment variables. Export a function that checks process.env values and returns different configurations for development, staging, and production. Set environment variables using 'MY_VARIABLE=value command' syntax, or use cross-env package on Windows.
Verify public app config contents
Run 'npx expo config --type public' to verify which configuration will be embedded in builds and updates and available at runtime. Run 'npx expo config' to display the final configuration that will be used after resolution.
Example: minimal app.json configuration
A minimal app.json configuration requires only name and slug fields: {"name": "My app", "slug": "my-app"}
Example: app.config.js with extra configuration
const myValue = 'My App';
module.exports = {
name: myValue,
version: process.env.MY_CUSTOM_PROJECT_VERSION || '1.0.0',
extra: {
fact: 'kittens are cool',
},
};
This example shows using variables, environment variables, and the extra field in app.config.js. The extra values are accessed at runtime with Constants.expoConfig.extra.fact.
Example: app.config.js function with config argument
module.exports = ({ config }) => {
console.log(config.name);
return {
...config,
};
};
This shows how to export a function in app.config.js that receives the static config (from app.json) as an argument and can modify it before returning the final config.
Example: app.config.ts with TypeScript
import { ExpoConfig, ConfigContext } from 'expo/config';
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
slug: 'my-app',
name: 'My App',
});
This shows the TypeScript version of app.config.ts with type annotations and autocomplete support.
Example: environment-based configuration switching
module.exports = () => {
if (process.env.MY_ENVIRONMENT === 'production') {
return {
/* your production config */
};
} else {
return {
/* your development config */
};
}
};
To use this, run commands with environment variables: MY_ENVIRONMENT=production eas update or npx cross-env MY_ENVIRONMENT=production eas update on Windows.
View native logs in Android Studio and Xcode
You can view native runtime logs in Android Studio and Xcode by compiling the native app locally. This requires native debugging.
Console logs appear in terminal during npx expo start
When you run npx expo start and connect a device, console logs will show up in the terminal process. These logs are sent from the runtime to Expo CLI over web sockets, so the results are lower fidelity than connecting dev tools directly to the engine.
System logs commands for Android and iOS
Use npx react-native log-android to show system logs for an Android device with adb logcat. Use npx react-native log-ios to show system logs for an iOS device. These commands work with npm, yarn, pnpm, and bun package managers.
High fidelity logs with Hermes and inspector
To view high fidelity logs and use advanced logging functions like console.table, you can create a development build with Hermes and connect the inspector.
Internal distribution for testing
If using EAS Build, go through Internal distribution to learn more about sharing your app with your team, beta testers, or running it on multiple test devices.
Development builds vs Expo Go compatibility
Development builds include all native code needed for production-quality apps and support libraries requiring native project configuration. Expo Go is a playground for quick prototyping and does not include all native code, so it is limited to libraries compatible with Expo Go. Any library compatible with React Native is compatible with Expo projects when using development builds.
React Native core libraries to import
React Native provides built-in primitives including components such as ActivityIndicator, TextInput, Text, ScrollView, and View. These core components and APIs are imported from the 'react-native' package. The React Native version corresponding to your Expo SDK version can be viewed in the Expo documentation.
How to import React Native core components
Import React Native core components directly from the 'react-native' package using the syntax: import { Text, View } from 'react-native';
Use npx expo install instead of npm install directly
Always use npx expo install instead of npm install or yarn add directly because Expo CLI can pick a compatible version of a library when possible and warn you about known incompatibilities.
Config plugins for additional native configuration
If a module needs additional native configuration, use config plugins. Some packages require a config plugin but don't have one yet; refer to the list of out-of-tree config plugins at https://github.com/expo/config-plugins/.
Build image selection and aliases
When selecting a build image in eas.json, you can use the full image name or one of these aliases: 'auto', 'latest', 'sdk-57', 'sdk-56', 'sdk-55', 'sdk-54', 'sdk-53', or 'sdk-52'. Using a specific image name guarantees a consistent environment with only minor updates. The 'auto' alias selects the image based on project configuration, Expo SDK version, and React Native version. The 'latest' alias gets the most up-to-date software versions. SDK aliases like 'sdk-57' are assigned to images best suited for that SDK version. SDK aliases are updated with every new SDK release, and the 'latest' alias is updated with every new image release. If you do not provide 'image' in eas.json, your build by default uses the 'auto' alias.
Configuring build environment overrides
Build images for each platform have one specific version of Node.js, Yarn, CocoaPods, Xcode, Ruby, Fastlane, and other tools. You can override some versions in eas.json. If no dedicated configuration option exists for what you need, you can use npm hooks to install or update system dependencies with apt-get or brew. These customizations are applied during the build and will increase build times.
eas-build-on-error hook
The eas-build-on-error hook is triggered at the end of the build process if the build failed.
eas-build-on-complete hook
The eas-build-on-complete hook is triggered at the end of the build process regardless of success or failure. The build's status can be checked using the EAS_BUILD_STATUS environment variable, which is set to either 'finished' or 'errored'.
eas-build-on-cancel hook
The eas-build-on-cancel hook is triggered if the build is canceled.
Platform-specific hook behavior using environment variable
To run lifecycle hook scripts only for specific platforms, check the EAS_BUILD_PLATFORM environment variable within the script. The value is 'android' for Android builds and 'ios' for iOS builds.
Example package.json with lifecycle hooks
Example package.json configuration using lifecycle hooks:
```json
{
"name": "my-app",
"scripts": {
"eas-build-pre-install": "echo 123",
"eas-build-post-install": "echo 456",
"eas-build-on-success": "echo 789",
"eas-build-on-error": "echo 012",
"eas-build-on-cancel": "echo 345",
"start": "expo start",
"test": "jest"
},
"dependencies": {
"expo": "{{expoSdkVersion}}"
}
}
```
EAS Build lifecycle npm hooks overview
EAS Build provides six lifecycle npm hooks that allow customization of the build process by running scripts before or after build stages. These hooks are configured in package.json scripts. Lifecycle hooks are not executed in custom builds and must be manually extracted and called during custom build steps.
eas-build-pre-install hook
The eas-build-pre-install hook is executed before EAS Build runs npm install.
eas-build-post-install hook behavior by platform
The eas-build-post-install hook behavior differs by platform. For Android, it runs once after npm install and npx expo prebuild (if needed) have both completed. For iOS, it runs once after npm install, npx expo prebuild (if needed), and pod install have all completed.
eas-build-on-success hook
The eas-build-on-success hook is triggered at the end of the build process if the build was successful.
Platform-specific hooks using shell script example
Example of a shell script for platform-specific hook behavior. In package.json, reference the script: `"eas-build-pre-install": "./pre-install"`. Then create a pre-install shell script that checks the EAS_BUILD_PLATFORM environment variable:
```bash
#!/bin/bash
if [[ "$EAS_BUILD_PLATFORM" == "android" ]]; then
echo "Run commands for Android builds here"
elif [[ "$EAS_BUILD_PLATFORM" == "ios" ]]; then
echo "Run commands for iOS builds here"
fi
```
Platform-specific hooks using Node.js script example
Example of a Node.js script for platform-specific hook behavior. In package.json, reference the script: `"eas-build-pre-install": "node pre-install.js"`. Then create a pre-install.js file that checks the process.env.EAS_BUILD_PLATFORM:
```js
if (process.env.EAS_BUILD_PLATFORM === 'android') {
console.log('Run commands for Android builds here');
} else if (process.env.EAS_BUILD_PLATFORM === 'ios') {
console.log('Run commands for iOS builds here');
}
```
Installing git-lfs in iOS pre-install hook example
Example pre-install script that installs git-lfs on macOS workers if not already installed, useful when git-lfs is required for certain CocoaPods:
```bash
if [[ "$EAS_BUILD_PLATFORM" == "ios" ]]; then
if brew list git-lfs > /dev/null 2>&1; then
echo "=====> git-lfs is already installed."
else
echo "=====> Installing git-lfs"
HOMEBREW_NO_AUTO_UPDATE=1 brew install git-lfs
git lfs install
fi
fi
```
Verify .npmrc configuration in build logs
To verify that .npmrc was created correctly, check the EAS Build logs and look for the Prepare project build phase, which will show if the .npmrc was successfully created and configured.