A .ts config is a program: the CLI evaluates it in Node on every run, so it can read process.env. That keeps the live-reload settings of The server Block out of release builds:
const liveUrl = process.env.CAP_LIVE_URL; // set only while developing
// ... inside the config object:
...(liveUrl && { server: { url: liveUrl, cleartext: true } }),CAP_LIVE_URL=http://localhost:5813 npx cap copy android
node -p "require('./android/app/src/main/assets/capacitor.config.json').server"
npx cap copy android
node -p "require('./android/app/src/main/assets/capacitor.config.json').server"...
{ url: 'http://localhost:5813', cleartext: true }
...
undefinedThe variable is read during copy and frozen into the APK's JSON; values the web code needs at run time belong in your bundler's variables (Vite 25,978 's import.meta.env).
capacitor.config.json holds the same keys as plain data, with no imports or logic. The .ts route has a trap: the CLI loads it through TypeScript's classic compiler API, which TypeScript 7 no longer exports. With typescript@7.0.2, npm 2,036 's latest, every sync failed with [error] Parsing capacitor.config.ts failed. The CLI's fallback, Node's built-in type stripping, needs Node.js 23.6 2,131 +, or 22.6+ with --experimental-strip-types; Ubuntu 225 's Node 22 build lacks it (ERR_NO_TYPESCRIPT). Pinning typescript@6 fixed sync at once.