Env and app.config

Environment Variables and app.json/app.config

app.json is the static configuration: name, slug, version, icons, the Android package and iOS bundleIdentifier (both com.example.booknest) and plugin settings. Prebuild turns it into manifest, Gradle 19,597 and Info.plist entries, so a change needs a new build. When a value must be computed, add app.config.js: Expo 6,418 reads app.json and passes it to your exported function as config.

app.config.js: a dynamic layer over app.jsonJSON
// Dynamic config: Expo passes the contents of app.json in as `config`.
module.exports = ({ config }) => ({
  ...config,
  name: process.env.APP_VARIANT === 'preview' ? 'BookNest (Preview)' : config.name,
  extra: {
    ...config.extra,
    catalogVersion: process.env.CATALOG_VERSION ?? 'dev',
  },
});

It runs in Node.js 2,131 at build time and sees the whole environment; the preview profile's APP_VARIANT renames the app for testers. Values under extra reach the app through Constants.expoConfig.extra from expo-constants. expo config prints the resolved result:

Resolving the configuration with and without variablesJSX
APP_VARIANT=preview CATALOG_VERSION=2026.09 npx expo config --type public --json \
  | node -e 'const c = JSON.parse(require("fs").readFileSync(0)); console.log(c.name, c.extra)'
npx expo config --type public --json \
  | node -e 'const c = JSON.parse(require("fs").readFileSync(0)); console.log(c.name, c.extra)'
Output
BookNest (Preview) { catalogVersion: '2026.09' }
BookNest { catalogVersion: 'dev' }

Application code has a second channel. Expo CLI loads .env and .env.local (keep the latter out of Git 1,932 ) and inlines every variable named EXPO_PUBLIC_* into the bundle. BookNest's .env holds EXPO_PUBLIC_API_URL=https://api.example.com/v1, Metro reports env: export EXPO_PUBLIC_API_URL at startup, and App.js reads process.env.EXPO_PUBLIC_API_URL. Inlining is text substitution, so write the full dotted name; process.env['EXPO_PUBLIC_API_URL'] and destructuring are not replaced. Anyone who unzips the APK can read these values, extra and the bundle, so keep secrets on your server.