Skip to main content
Quick answers to frequently asked questions about Expo development.

Getting Started

Expo is a framework and platform for building React Native apps. It provides:
  • A comprehensive SDK with native modules
  • Development tools and CLI
  • Build and deployment services (EAS)
  • Over-the-air updates
  • A managed workflow for easier development
Expo makes React Native development faster and more accessible while maintaining full native capabilities.
No, you can build complete apps using only JavaScript/TypeScript. However:
  • Managed workflow: No native knowledge required
  • Custom native code: You can drop down to native code when needed
  • Config plugins: Many native customizations can be done without writing native code
You can start without native knowledge and learn as you need it.
Yes! You can:
  1. Install Expo packages in any React Native app:
    npx install-expo-modules
    
  2. Use individual Expo modules:
    npx expo install expo-camera expo-location
    
  3. Adopt Expo gradually without rewriting your app
See the integration guide.
Expo Go:
  • Sandbox app with pre-installed Expo SDK
  • Quick prototyping and learning
  • Limited to Expo SDK modules
  • No custom native code
Development Builds:
  • Your actual app with development tools
  • Supports any native library
  • Custom native code allowed
  • More flexibility, closer to production
For production apps, use development builds. Expo Go is great for learning and quick tests.

Project Setup

# Create a new project
npx create-expo-app my-app

# Navigate to project
cd my-app

# Start development server
npx expo start
This creates a new Expo project with TypeScript support and recommended configurations.
app.json:
  • Static JSON configuration
  • Simple and straightforward
  • No logic or dynamic values
{
  "expo": {
    "name": "My App",
    "version": "1.0.0"
  }
}
app.config.js:
  • Dynamic JavaScript configuration
  • Access environment variables
  • Conditional logic
  • Function-based configuration
export default ({ config }) => ({
  ...config,
  name: process.env.APP_NAME || 'My App',
});
If both exist, app.config.js takes precedence.
Expo supports TypeScript out of the box:
  1. Rename files from .js to .tsx or .ts
  2. Create tsconfig.json:
    npx expo customize tsconfig.json
    
  3. Install types:
    npm install --save-dev @types/react @types/react-native
    
  4. Start developing with TypeScript!
No additional configuration needed.

Development

Using Expo Go:
  1. Install Expo Go from the App Store or Google Play
  2. Run npx expo start
  3. Scan the QR code with your device
Using a development build:
  1. Build your app: eas build --profile development
  2. Install the build on your device
  3. Run npx expo start --dev-client
  4. Open the app on your device
See Device Setup.
React DevTools:
npx expo start
# Press 'j' to open debugger
Console Logs:
  • Logs appear in the terminal automatically
  • Use console.log(), console.warn(), console.error()
React Native Debugger:
  1. Install React Native Debugger
  2. Press ‘j’ in the terminal
  3. Debug in the standalone app
VS Code Debugging:
  1. Install React Native Tools extension
  2. Configure launch.json
  3. Set breakpoints and debug
See Debugging Guide.
# Clear Metro cache
npx expo start --clear

# Or manually delete caches
rm -rf node_modules/.cache
rm -rf .metro

# For native projects
cd ios && pod cache clean --all && cd ..
cd android && ./gradlew clean && cd ..
Clear cache when you have bundling issues or after updating dependencies.
Common causes:
  1. Metro cache: Clear with --clear flag
  2. Large dependencies: Check bundle size
  3. Network connection: Use LAN or localhost
  4. Debug mode overhead: Normal in development
  5. Source maps: Slow but necessary for debugging
Production builds are much faster. To test performance, build a release version.

Configuration

  1. Install expo-font:
    npx expo install expo-font
    
  2. Load fonts:
    import { useFonts } from 'expo-font';
    
    export default function App() {
      const [loaded] = useFonts({
        'Custom-Font': require('./assets/fonts/CustomFont.ttf'),
      });
      
      if (!loaded) return null;
      
      return <Text style={{ fontFamily: 'Custom-Font' }}>Hello</Text>;
    }
    
  3. Fonts load automatically on subsequent launches
See Font Guide.
  1. Create .env file (optional):
    EXPO_PUBLIC_API_URL=https://api.example.com
    
  2. Access in code:
    const apiUrl = process.env.EXPO_PUBLIC_API_URL;
    
  3. Or use app.config.js:
    export default {
      expo: {
        extra: {
          apiUrl: process.env.API_URL,
        },
      },
    };
    
    Then access via Constants:
    import Constants from 'expo-constants';
    const apiUrl = Constants.expoConfig?.extra?.apiUrl;
    
Only variables prefixed with EXPO_PUBLIC_ are available in app code.
App Icon:
  1. Create a 1024x1024 PNG image
  2. Update app.json:
    {
      "expo": {
        "icon": "./assets/icon.png"
      }
    }
    
Splash Screen:
  1. Create your splash image
  2. Update app.json:
    {
      "expo": {
        "splash": {
          "image": "./assets/splash.png",
          "backgroundColor": "#ffffff",
          "resizeMode": "contain"
        }
      }
    }
    
  3. Rebuild your app
See App Icon and Splash Screen guides.

Building & Deployment

Use EAS Build:
  1. Install EAS CLI:
    npm install -g eas-cli
    
  2. Configure EAS:
    eas build:configure
    
  3. Build for iOS and Android:
    eas build --platform all
    
  4. Download and test the build
See EAS Build.
Yes, for native builds:iOS:
npx expo prebuild
cd ios
xcodebuild -workspace MyApp.xcworkspace -scheme MyApp
Android:
npx expo prebuild
cd android
./gradlew assembleRelease
However, EAS Build provides:
  • Cloud building (no native toolchain needed)
  • Consistent environment
  • Easier signing and credentials management
  • Build caching
  1. Build your app:
    eas build --platform all
    
  2. Submit to stores:
    eas submit --platform ios
    eas submit --platform android
    
EAS handles credentials, signing, and submission automatically.See App Stores.
Over-the-air (OTA) updates let you push JavaScript changes without app store review:
  1. Configure updates:
    {
      "expo": {
        "updates": {
          "url": "https://u.expo.dev/your-project-id"
        }
      }
    }
    
  2. Publish update:
    eas update --branch production
    
  3. Users get the update on next app launch
Limitations:
  • JavaScript/assets only (no native code changes)
  • Requires expo-updates package
  • Users must have compatible native code
See EAS Update.

Native Modules & Libraries

Yes! You can use:
  1. Expo SDK modules - Pre-configured and tested
  2. React Native core - All React Native features
  3. Community libraries - Most work out of the box
  4. Native modules - With custom development builds
Libraries with native code require:
  • Config plugins (for some), or
  • Custom development builds
Search compatibility at React Native Directory.
  1. Install the library:
    npx expo install react-native-library
    
  2. Check if it needs a config plugin:
    {
      "expo": {
        "plugins": ["react-native-library"]
      }
    }
    
  3. Rebuild your app:
    npx expo prebuild --clean
    npx expo run:ios
    
Some libraries work without plugins; others require custom native code.
Config plugins modify native projects during prebuild:
{
  "expo": {
    "plugins": [
      "expo-camera",
      [
        "expo-location",
        {
          "locationAlwaysAndWhenInUsePermission": "Allow location access"
        }
      ]
    ]
  }
}
They let you customize native code without writing it manually.See Config Plugins.

Common Errors

Solutions:
  1. Clear cache:
    npx expo start --clear
    
  2. Check port availability (default 8081)
  3. Kill existing Metro processes:
    lsof -ti:8081 | xargs kill -9
    
  4. Reinstall dependencies:
    rm -rf node_modules
    npm install
    
Causes:
  • Missing dependency
  • Incorrect import path
  • Cache issue
  • Case sensitivity (especially on Linux)
Solutions:
  1. Install missing package:
    npx expo install package-name
    
  2. Clear cache:
    npx expo start --clear
    
  3. Check import paths:
    // Wrong
    import Component from './Component';
    
    // Correct (if file is Component.tsx)
    import Component from './Component';
    
  4. Restart Metro
Causes:
  • Native module not linked
  • Module not installed
  • Incompatible versions
Solutions:
  1. For new modules, rebuild:
    npx expo prebuild --clean
    npx expo run:ios
    
  2. Check module installation:
    npx expo install --check
    
  3. Verify config plugin is added:
    {
      "expo": {
        "plugins": ["module-name"]
      }
    }
    

Performance

General optimizations:
  1. Use Hermes:
    { "expo": { "jsEngine": "hermes" } }
    
  2. Optimize images:
    • Use appropriate sizes
    • Compress images
    • Use WebP format
  3. Lazy load components:
    const Component = React.lazy(() => import('./Component'));
    
  4. Memoize expensive computations:
    const value = useMemo(() => expensiveOperation(), [deps]);
    
  5. Use FlatList for long lists:
    <FlatList
      data={items}
      renderItem={renderItem}
      windowSize={5}
    />
    
See Performance.
  1. Analyze bundle:
    npx expo export --dump-sourcemap
    
  2. Remove unused dependencies:
    npm uninstall unused-package
    
  3. Tree shake properly:
    // Good
    import { specific } from 'library';
    
    // Bad
    import * as library from 'library';
    
  4. Use Hermes (smaller bundle)
  5. Enable minification (production only)

Monorepos

Yes! Expo works with:
  • Yarn Workspaces
  • npm Workspaces
  • pnpm
  • Turborepo
  • Nx
Example Metro config:
const path = require('path');
const { getDefaultConfig } = require('expo/metro-config');

const projectRoot = __dirname;
const workspaceRoot = path.resolve(projectRoot, '../..');

const config = getDefaultConfig(projectRoot);

config.watchFolders = [workspaceRoot];
config.resolver.nodeModulesPaths = [
  path.resolve(projectRoot, 'node_modules'),
  path.resolve(workspaceRoot, 'node_modules'),
];

module.exports = config;
See Monorepos.

Getting Help

See Community Resources.