agentsclimarketplace

Configuring expo

Skill rahulkeerthi/expo-toolkit/skills/configuring-expo

Expo app.config.js patterns, environment variables, and project configuration. Use when setting up dynamic config, environment-specific bundle IDs, or troubleshooting Expo project issues.From its SKILL.md

Install
npx -y skills add rahulkeerthi/expo-toolkit --skill configuring-expo

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

11.2 KB, as published. Nobody here has run it

Expo Configuration Reference

app.config.js vs app.json

Use app.config.js (recommended):

  • Dynamic configuration based on environment
  • Conditional logic for different build variants
  • Access to environment variables
  • Can import from other files

Use app.json:

  • Simple static configuration
  • No environment-specific needs
  • Quick prototyping

Converting app.json to app.config.js

If you have an existing app.json:

// app.config.js
export default ({ config }) => {
  return {
    ...config,
    // Your customizations here
  };
};

Or start fresh:

// app.config.js
export default {
  name: "My App",
  slug: "my-app",
  // ... rest of config
};

Environment-Specific Bundle IDs

The recommended pattern for running dev/preview/production builds side-by-side on the same device:

// app.config.js
const IS_DEV = process.env.APP_VARIANT === 'development';
const IS_PREVIEW = process.env.APP_VARIANT === 'preview';

const getUniqueIdentifier = () => {
  if (IS_DEV) {
    return 'com.yourcompany.yourapp.dev';
  }
  if (IS_PREVIEW) {
    return 'com.yourcompany.yourapp.preview';
  }
  return 'com.yourcompany.yourapp';
};

const getAppName = () => {
  if (IS_DEV) {
    return 'YourApp (Dev)';
  }
  if (IS_PREVIEW) {
    return 'YourApp (Preview)';
  }
  return 'YourApp';
};

export default {
  name: getAppName(),
  slug: 'your-app',
  version: '1.0.0',
  orientation: 'portrait',
  icon: './assets/icon.png',
  userInterfaceStyle: 'automatic',
  splash: {
    image: './assets/splash.png',
    resizeMode: 'contain',
    backgroundColor: '#ffffff',
  },
  assetBundlePatterns: ['**/*'],
  ios: {
    supportsTablet: true,
    bundleIdentifier: getUniqueIdentifier(),
  },
  android: {
    adaptiveIcon: {
      foregroundImage: './assets/adaptive-icon.png',
      backgroundColor: '#ffffff',
    },
    package: getUniqueIdentifier(),
  },
  web: {
    favicon: './assets/favicon.png',
  },
  extra: {
    eas: {
      projectId: 'your-project-id',
    },
  },
};

Corresponding eas.json

{
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal",
      "env": {
        "APP_VARIANT": "development"
      }
    },
    "preview": {
      "distribution": "internal",
      "env": {
        "APP_VARIANT": "preview"
      }
    },
    "production": {
      "env": {
        "APP_VARIANT": "production"
      }
    }
  }
}

Environment Variables

Build-time vs Runtime Variables

TypePrefixAvailableUse Case
Build-timeNoneapp.config.js onlyBundle ID, app name
RuntimeEXPO_PUBLIC_App codeAPI URLs, feature flags

Build-time Variables

Used in app.config.js during build:

// app.config.js
const API_URL = process.env.API_URL || 'https://api.default.com';

export default {
  // ...
  extra: {
    apiUrl: API_URL,
  },
};

Set in eas.json:

{
  "build": {
    "production": {
      "env": {
        "API_URL": "https://api.production.com"
      }
    }
  }
}

Runtime Variables (Client-side)

Variables accessible in your app code:

// In eas.json
{
  "build": {
    "production": {
      "env": {
        "EXPO_PUBLIC_API_URL": "https://api.example.com"
      }
    }
  }
}

// In your app code
const apiUrl = process.env.EXPO_PUBLIC_API_URL;

IMPORTANT: EXPO_PUBLIC_ variables are embedded in the JS bundle. Never use for secrets!

Local Development (.env)

For local development, use .env files:

# .env.local (gitignored)
EXPO_PUBLIC_API_URL=http://localhost:3000

Install expo-env:

npx expo install expo-env

Complete app.config.js Template

// app.config.js
const IS_DEV = process.env.APP_VARIANT === 'development';
const IS_PREVIEW = process.env.APP_VARIANT === 'preview';

const getUniqueIdentifier = () => {
  if (IS_DEV) return 'com.yourcompany.yourapp.dev';
  if (IS_PREVIEW) return 'com.yourcompany.yourapp.preview';
  return 'com.yourcompany.yourapp';
};

const getAppName = () => {
  if (IS_DEV) return 'YourApp (Dev)';
  if (IS_PREVIEW) return 'YourApp (Preview)';
  return 'YourApp';
};

export default {
  // Basic Info
  name: getAppName(),
  slug: 'your-app',
  version: '1.0.0',
  orientation: 'portrait',

  // Assets
  icon: './assets/icon.png',
  splash: {
    image: './assets/splash.png',
    resizeMode: 'contain',
    backgroundColor: '#ffffff',
  },
  assetBundlePatterns: ['**/*'],

  // Appearance
  userInterfaceStyle: 'automatic',

  // iOS Configuration
  ios: {
    supportsTablet: false, // or true for iPad support
    bundleIdentifier: getUniqueIdentifier(),
    buildNumber: '1',
    infoPlist: {
      NSCameraUsageDescription: 'This app uses the camera to...',
      NSPhotoLibraryUsageDescription: 'This app accesses photos to...',
      // Add other permissions as needed
    },
    config: {
      usesNonExemptEncryption: false, // If no custom encryption
    },
  },

  // Android Configuration
  android: {
    package: getUniqueIdentifier(),
    versionCode: 1,
    adaptiveIcon: {
      foregroundImage: './assets/adaptive-icon.png',
      backgroundColor: '#ffffff',
    },
    permissions: [
      // Add permissions as needed
      // 'android.permission.CAMERA',
      // 'android.permission.READ_EXTERNAL_STORAGE',
    ],
  },

  // Web Configuration (if applicable)
  web: {
    favicon: './assets/favicon.png',
    bundler: 'metro',
  },

  // Plugins
  plugins: [
    // Add Expo plugins here
    // 'expo-camera',
    // ['expo-image-picker', { photosPermission: '...' }],
  ],

  // Extra Configuration
  extra: {
    eas: {
      projectId: 'your-eas-project-id',
    },
    // Add custom config accessible via expo-constants
  },

  // Updates (EAS Update)
  updates: {
    url: 'https://u.expo.dev/your-project-id',
  },
  runtimeVersion: {
    policy: 'appVersion',
  },

  // Owner (for EAS)
  owner: 'your-expo-username',
};

Key Configuration Fields

App Identity

FieldDescriptionExample
nameDisplay name"My App"
slugURL-friendly name"my-app"
versionUser-facing version"1.0.0"
ios.bundleIdentifieriOS bundle ID"com.company.app"
android.packageAndroid package name"com.company.app"

Versioning

FieldPlatformDescription
versionBothSemantic version shown to users
ios.buildNumberiOSInternal build number (string)
android.versionCodeAndroidInternal version code (integer)

Tip: Use autoIncrement in EAS to manage build numbers automatically.

Assets

FieldSizeFormat
icon1024x1024PNG
splash.image1284x2778 (or similar)PNG
android.adaptiveIcon.foregroundImage1024x1024PNG
web.favicon48x48PNG

Permissions

iOS Permissions (infoPlist)

Add to ios.infoPlist:

infoPlist: {
  NSCameraUsageDescription: 'Required for taking photos',
  NSPhotoLibraryUsageDescription: 'Required for selecting photos',
  NSLocationWhenInUseUsageDescription: 'Required for location features',
  NSMicrophoneUsageDescription: 'Required for recording audio',
  NSFaceIDUsageDescription: 'Required for secure authentication',
}

Android Permissions

Add to android.permissions:

permissions: [
  'android.permission.CAMERA',
  'android.permission.READ_EXTERNAL_STORAGE',
  'android.permission.WRITE_EXTERNAL_STORAGE',
  'android.permission.ACCESS_FINE_LOCATION',
  'android.permission.RECORD_AUDIO',
]

Config Plugins

For native configuration that goes beyond standard options:

plugins: [
  // Simple plugin
  'expo-camera',

  // Plugin with options
  ['expo-image-picker', {
    photosPermission: 'Allow access to select photos',
  }],

  // Custom plugin (local)
  './plugins/my-plugin.js',
]

Common Plugins

PluginPurpose
expo-cameraCamera access
expo-image-pickerPhoto library access
expo-notificationsPush notifications
expo-locationLocation services
expo-avAudio/video playback
expo-build-propertiesNative build settings

EAS Update Configuration

For over-the-air updates:

export default {
  // ...
  updates: {
    url: 'https://u.expo.dev/your-project-id',
    fallbackToCacheTimeout: 0,
  },
  runtimeVersion: {
    policy: 'appVersion', // or 'sdkVersion', 'fingerprint', or explicit version
  },
};

Runtime Version Policies

PolicyWhen Updates Apply
appVersionSame version in config
sdkVersionSame Expo SDK version
fingerprintSame native code fingerprint
"1.0.0"Explicit version string

Expo Doctor

Run diagnostics to check project health:

npx expo doctor

Common Issues Detected

IssueSolution
SDK version mismatchUpdate packages: npx expo install --fix
Deprecated packagesMigrate to recommended alternatives
Invalid configFix app.config.js / app.json
Missing peer dependenciesInstall missing packages
Native module issuesRun npx expo prebuild --clean

Fixing Common Problems

# Fix package version mismatches
npx expo install --fix

# Clear caches and reinstall
rm -rf node_modules
rm -rf .expo
npm install

# Regenerate native projects (if using prebuild)
npx expo prebuild --clean

Troubleshooting

Config Not Updating

# Clear Metro cache
npx expo start --clear

# Clear all caches
rm -rf node_modules/.cache
rm -rf .expo

Environment Variables Not Working

  1. Ensure variable is set in eas.json for the correct profile
  2. For runtime access, use EXPO_PUBLIC_ prefix
  3. Restart Metro bundler after changes

Bundle ID Conflicts

If you get "app already installed" errors when switching environments:

  • Ensure each variant has a unique bundle ID
  • Uninstall the old app before installing new variant
  • Check APP_VARIANT is set correctly in build profile

Best Practices

  1. Use app.config.js - Dynamic config enables environment variants
  2. Separate bundle IDs - Run dev/preview/production side-by-side
  3. Use APP_VARIANT pattern - Clean separation of environments
  4. Never commit secrets - Use EAS Secrets for sensitive data
  5. Run expo doctor regularly - Catch issues early
  6. Keep SDK updated - Security and compatibility
  7. Document permissions - Explain why each permission is needed
  8. Use config plugins - For native customisation beyond defaults

CLI Quick Reference

# Start development server
npx expo start

# Start with clean cache
npx expo start --clear

# Run diagnostics
npx expo doctor

# Fix package versions
npx expo install --fix

# Install a package
npx expo install package-name

# Generate native projects
npx expo prebuild

# Clean and regenerate native projects
npx expo prebuild --clean

# Export for web
npx expo export --platform web

# View config
npx expo config

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.