Build-Time Config
A single variable, APP_ENV (development | preview | production),
drives a dynamic app.config.ts. Each environment gets its own bundle id,
package name, scheme, and deep-link host — so a dev build, a test build, and the
store build can all sit on the same phone at once.
// app.config.ts
const APP_ENV = process.env.APP_ENV ?? "development";
function getDynamicAppConfig(env: string) {
const base = { name: "MyApp", scheme: "myapp" };
if (env === "production") {
return {
...base,
ios: { bundleIdentifier: "com.yourorg.app" },
android: { package: "com.yourorg.app" },
};
}
if (env === "preview") {
return {
...base,
name: "MyApp (preview)",
scheme: "myapp.preview",
ios: { bundleIdentifier: "com.yourorg.app.preview" },
android: { package: "com.yourorg.app.preview" },
};
}
return {
...base,
name: "MyApp (dev)",
scheme: "myapp.dev",
ios: { bundleIdentifier: "com.yourorg.app.dev" },
android: { package: "com.yourorg.app.dev" },
};
}
export default () => ({ expo: getDynamicAppConfig(APP_ENV) });
APP_ENV is set per EAS build profile in eas.json (the env block), so
you never set it by hand — picking a profile picks the environment. See
Build & Distribution → Concepts.
Locally, env-cmd -f .env.<env> loads that environment’s runtime values while
APP_ENV (which each .env.<env> file can define) selects the matching native
config here — the two layers stay aligned. See Getting Started → Environment
Variables.
Pair this with appVersionSource: "remote" and autoIncrement: true in
eas.json so EAS manages build numbers for you instead of hand-editing them.