From expo
Configures Expo apps using app.json, app.config.js/ts, and EAS for metadata, plugins, build settings, and environment variables.
How this skill is triggered — by the user, by Claude, or both
Slash command
/expo:expo-configThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use this skill when configuring Expo applications using app.json, app.config.js/ts, and EAS (Expo Application Services) configuration files.
Use this skill when configuring Expo applications using app.json, app.config.js/ts, and EAS (Expo Application Services) configuration files.
Basic static configuration:
{
"expo": {
"name": "MyApp",
"slug": "my-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": "com.mycompany.myapp",
"buildNumber": "1"
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"package": "com.mycompany.myapp",
"versionCode": 1
},
"web": {
"favicon": "./assets/favicon.png"
}
}
}
Use JavaScript for dynamic configuration:
export default ({ config }) => ({
...config,
name: process.env.APP_NAME || 'MyApp',
slug: 'my-app',
version: '1.0.0',
extra: {
apiUrl: process.env.API_URL,
environment: process.env.NODE_ENV,
},
ios: {
bundleIdentifier:
process.env.NODE_ENV === 'production'
? 'com.mycompany.myapp'
: 'com.mycompany.myapp.dev',
},
android: {
package:
process.env.NODE_ENV === 'production'
? 'com.mycompany.myapp'
: 'com.mycompany.myapp.dev',
},
});
Use TypeScript for type-safe config:
// app.config.ts
import { ExpoConfig, ConfigContext } from '@expo/config';
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
name: 'MyApp',
slug: 'my-app',
version: '1.0.0',
orientation: 'portrait',
icon: './assets/icon.png',
splash: {
image: './assets/splash.png',
resizeMode: 'contain',
backgroundColor: '#ffffff',
},
plugins: [
'expo-router',
[
'expo-camera',
{
cameraPermission: 'Allow $(PRODUCT_NAME) to access your camera.',
},
],
],
extra: {
apiUrl: process.env.API_URL,
},
});
Use environment-specific configuration:
// app.config.ts
import { ExpoConfig, ConfigContext } from '@expo/config';
const IS_DEV = process.env.NODE_ENV === 'development';
const IS_PROD = process.env.NODE_ENV === 'production';
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
name: IS_PROD ? 'MyApp' : 'MyApp (Dev)',
slug: 'my-app',
extra: {
apiUrl: IS_PROD
? 'https://api.myapp.com'
: 'https://dev-api.myapp.com',
environment: process.env.NODE_ENV,
eas: {
projectId: process.env.EAS_PROJECT_ID,
},
},
ios: {
bundleIdentifier: IS_PROD
? 'com.mycompany.myapp'
: 'com.mycompany.myapp.dev',
},
android: {
package: IS_PROD
? 'com.mycompany.myapp'
: 'com.mycompany.myapp.dev',
},
});
Configure Expo plugins:
{
"expo": {
"plugins": [
"expo-router",
[
"expo-camera",
{
"cameraPermission": "Allow $(PRODUCT_NAME) to access camera"
}
],
[
"expo-location",
{
"locationAlwaysAndWhenInUsePermission": "Allow $(PRODUCT_NAME) to use your location"
}
],
[
"expo-notifications",
{
"icon": "./assets/notification-icon.png",
"color": "#ffffff"
}
]
]
}
}
Configure EAS builds with eas.json:
{
"cli": {
"version": ">= 5.9.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"ios": {
"simulator": true
}
},
"preview": {
"distribution": "internal",
"ios": {
"simulator": false
},
"android": {
"buildType": "apk"
}
},
"production": {
"autoIncrement": true
}
},
"submit": {
"production": {}
}
}
Access configuration at runtime:
import Constants from 'expo-constants';
export function App() {
const apiUrl = Constants.expoConfig?.extra?.apiUrl;
const environment = Constants.expoConfig?.extra?.environment;
console.log('API URL:', apiUrl);
console.log('Environment:', environment);
return <View />;
}
// app.config.ts
import { ExpoConfig, ConfigContext } from '@expo/config';
type Environment = 'development' | 'staging' | 'production';
const ENV: Environment = (process.env.APP_ENV as Environment) || 'development';
const config: Record<Environment, { apiUrl: string; name: string }> = {
development: {
apiUrl: 'http://localhost:3000',
name: 'MyApp (Dev)',
},
staging: {
apiUrl: 'https://staging-api.myapp.com',
name: 'MyApp (Staging)',
},
production: {
apiUrl: 'https://api.myapp.com',
name: 'MyApp',
},
};
export default ({ config: baseConfig }: ConfigContext): ExpoConfig => ({
...baseConfig,
name: config[ENV].name,
slug: 'my-app',
extra: {
apiUrl: config[ENV].apiUrl,
environment: ENV,
},
});
// app.config.ts
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
extra: {
features: {
enableNewUI: process.env.ENABLE_NEW_UI === 'true',
enableAnalytics: process.env.ENABLE_ANALYTICS !== 'false',
enableBetaFeatures: process.env.ENABLE_BETA === 'true',
},
},
});
// In code
import Constants from 'expo-constants';
const features = Constants.expoConfig?.extra?.features;
if (features?.enableNewUI) {
// Show new UI
}
{
"expo": {
"ios": {
"icon": "./assets/icon-ios.png",
"splash": {
"image": "./assets/splash-ios.png"
}
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon-android.png",
"backgroundColor": "#ffffff"
},
"splash": {
"image": "./assets/splash-android.png"
}
}
}
}
{
"expo": {
"scheme": "myapp",
"slug": "my-app",
"web": {
"bundler": "metro"
},
"ios": {
"associatedDomains": ["applinks:myapp.com"]
},
"android": {
"intentFilters": [
{
"action": "VIEW",
"autoVerify": true,
"data": [
{
"scheme": "https",
"host": "myapp.com",
"pathPrefix": "/app"
}
],
"category": ["BROWSABLE", "DEFAULT"]
}
]
}
}
}
// app.config.ts
import packageJson from './package.json';
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
version: packageJson.version,
ios: {
buildNumber: process.env.IOS_BUILD_NUMBER || '1',
},
android: {
versionCode: parseInt(process.env.ANDROID_VERSION_CODE || '1', 10),
},
});
// Bad - Secrets in config
export default {
extra: {
apiKey: 'sk_live_1234567890',
apiSecret: 'secret_key_here',
},
};
// Good - Use environment variables
export default {
extra: {
apiKey: process.env.API_KEY,
// Never commit secrets
},
};
// Bad - Can't use dynamic values in app.json
{
"expo": {
"name": process.env.APP_NAME
}
}
// Good - Use app.config.js/ts
// app.config.ts
export default {
name: process.env.APP_NAME || 'MyApp',
};
// Bad - Missing required fields
{
"expo": {
"name": "MyApp",
"slug": "my-app"
}
}
// Good - Include all required fields
{
"expo": {
"name": "MyApp",
"slug": "my-app",
"ios": {
"bundleIdentifier": "com.mycompany.myapp"
},
"android": {
"package": "com.mycompany.myapp"
}
}
}
// Bad - Mixing static and dynamic
// app.json
{
"expo": {
"name": "MyApp"
}
}
// app.config.js also exists - creates conflicts
// Good - Use one or the other
// Either app.json for static config
// Or app.config.js/ts for dynamic config
npx claudepluginhub thebushidocollective/han --plugin expoGuides Expo project setup with managed workflow, EAS Build, development builds, and config plugins for React Native apps.
Expo and React Native patterns for mobile development with EAS Build, SDK integration, and native module configuration. Use when building Expo apps, configuring native features, or setting up app signing for store deployment.
Builds and deploys Expo apps with EAS Build, covering eas.json profiles for development, preview, production; secrets, env vars, hooks, and multi-environment configs.