Copy
Trading Bots
Events
More

Set Up Multi-Environment Mobile Builds with Expo

2026/07/16 06:22Browse 0

Answer Box: A single environment variable (`APP_VARIANT`) in an Expo React Native project can drive separate app names, bundle IDs, API URLs, and Firebase configs for development, preview (staging), and production — allowing all three builds to coexist on the same device without manual switching or code changes.

The Problem: Mobile Apps Can't Run Side by Side by Default

Web developers are used to running staging and production in separate browser tabs, but mobile apps don't work that way. Without a multi-environment setup, you end up with one `app.json` that holds a single API URL, one bundle ID, and one Firebase config. That means you have to uninstall the production app before testing a staging build, you can't compare behavior side by side, and you risk shipping a production build that accidentally points to a dev API. QA teams also get stuck waiting for developers to manually swap values before each build.

The root cause is that every app on a device is uniquely identified by its bundle ID (iOS) or package name (Android). If staging and production share the same identifier, the operating system treats them as the same app and forces you to replace one with the other.

The Fix: Branch Everything on a Single Environment Variable

The solution is to let your build configuration branch on a single environment variable — `APP_VARIANT` — and then use that variable to set unique bundle IDs, app names, icons, Firebase configs, and API URLs for each environment. This is done in two places: `eas.json` defines the build profiles and injects the variable, while `app.config.ts` reads it at build time to dynamically generate the final config.

Step 1: Define Build Profiles in eas.json

In your `eas.json`, create three profiles — `development`, `preview`, and `production` — each with its own `env.APP_VARIANT` value. The development profile should set `"developmentClient": true` so it uses a dev client build; preview and production use internal distribution or store submission. Each profile also gets its own OTA update channel, so updates land on the correct audience. When you run a build, you specify the profile, and EAS injects the matching `APP_VARIANT` into the environment.

Step 2: Make app.config.ts Dynamic

A static `app.json` cannot read environment variables, so switch to `app.config.ts`. This file exports a function that receives the build context and returns an `ExpoConfig` object. Inside the function, read `process.env.APP_VARIANT` and use it to select the correct bundle ID, app name, app icon, and Firebase config file path for each variant. For example, the production bundle ID might be `com.example.app`, while the development variant becomes `com.example.app.dev`. This guarantees that each build gets a unique identifier, allowing all three to be installed on the same device simultaneously.

Step 3: Keep app.json for Static Defaults

Your `app.json` still holds everything that does not change between environments — permissions, splash screen configuration, plugins, and so on. The dynamic `app.config.ts` spreads over these defaults and overrides only the fields that need to vary. This keeps your project organized and avoids duplicating configuration.

Step 4: Manage Secrets with EAS Environment Variables

Never commit API keys, socket URLs, or third-party tokens to git. Instead, store them as encrypted environment variables on the EAS server using `eas env:push`. You can pull them down locally with `eas env:pull`. In your code, reference these variables using the `EXPO_PUBLIC_` prefix (e.g., `EXPO_PUBLIC_API_URL`), which tells Expo to expose them to the client bundle at runtime. Each environment can have its own set of secrets, keeping staging and production data fully isolated.

Step 5: Validate Environment Variables at Runtime

Reading `process.env` directly across the app is fragile — a missing variable silently becomes `undefined`. Use a schema validation library like Zod to define the expected shape of your environment variables and validate them all at app startup. If any variable is missing or invalid, the app throws a clear error listing exactly what is wrong. This catches configuration mistakes early, before they cause subtle bugs in production.

The Result: One Command, Correct Build Every Time

Once this setup is in place, you (or your QA team) can run a single command — `eas build --profile preview` — and get a build that is correctly wired for the staging environment. The same device can have the production app installed from the App Store, a preview build from EAS, and a development client build, all with distinct names and icons. No manual URL swapping, no uninstalling and reinstalling, and no risk of mixing up environments.

Disclaimer: This page may contain third-party information and does not necessarily reflect BYDFi's views or opinions. This content is for general reference only and does not constitute any representation, warranty, financial advice, or investment advice. BYDFi is not responsible for any errors, omissions, or any results arising from the use of such information. Virtual asset investments involve risks. Please carefully evaluate the risks of the product and your risk tolerance based on your financial situation. For more information, please refer to our Terms of Use and Risk Disclosure.