Skip to main content
Development builds come with powerful debugging capabilities. This guide covers all available debugging tools and workflows.

Available Debugging Tools

Expo provides multiple debugging methods:
ToolPurposePlatform
Chrome DevToolsJavaScript debugging, console, networkAll
React DevToolsComponent hierarchy, props, stateAll
Expo DevToolsDev menu, inspector, pluginsAll
Xcode DebuggerNative iOS codeiOS
Android StudioNative Android codeAndroid
Network InspectorHTTP requests/responsesAll

JavaScript Debugging

Chrome DevTools

The primary tool for debugging JavaScript code.
1

Enable debugging

# Start with debugging enabled
npx expo start --dev-client
In your development build:
  1. Shake device or press Cmd+D (iOS) / Cmd+M (Android)
  2. Select “Debug with Chrome”
2

Open DevTools

Chrome opens automatically at:
http://localhost:8081/debugger-ui/
Or manually open Chrome and press Cmd+Option+J (Mac) / Ctrl+Shift+J (Windows/Linux).
3

Set breakpoints

app/index.tsx
export default function HomeScreen() {
  const [count, setCount] = useState(0);
  
  const handlePress = () => {
    debugger; // Execution pauses here
    setCount(count + 1);
    console.log('Count:', count);
  };
  
  return (
    <Button onPress={handlePress} title="Increment" />
  );
}

Console API

Use console methods for debugging:
// Basic logging
console.log('Simple message');
console.log('Multiple', 'arguments', { data: 'object' });

// Styled output
console.warn('Warning message');
console.error('Error message');
console.info('Info message');

// Grouped output
console.group('User Data');
console.log('Name:', user.name);
console.log('Email:', user.email);
console.groupEnd();

// Timing
console.time('fetch');
await fetchData();
console.timeEnd('fetch'); // fetch: 234ms

// Tables
console.table([{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]);

// Assert
console.assert(count > 0, 'Count must be positive');

// Stack trace
console.trace('Trace point');

Source Maps

Ensure source maps are enabled for readable stack traces:
app.json
{
  "expo": {
    "packagerOpts": {
      "sourceExts": ["js", "jsx", "ts", "tsx"],
      "sourceMaps": true
    }
  }
}

React DevTools

Inspect React component tree, props, and state.
1

Install React DevTools

npm install -g react-devtools
2

Start React DevTools

# In a separate terminal
react-devtools
A standalone window opens.
3

Connect to app

# Start your app
npx expo start --dev-client
React DevTools automatically connects to your running app.
4

Inspect components

  • Components tab: View component hierarchy
  • Profiler tab: Measure render performance
  • Select components to inspect props and state
  • Edit props/state in real-time

Component Inspection

app/components/UserProfile.tsx
import { useState } from 'react';

export function UserProfile({ userId }: { userId: string }) {
  const [loading, setLoading] = useState(false);
  const [user, setUser] = useState(null);
  
  // In React DevTools:
  // - See UserProfile in component tree
  // - Inspect props: { userId: "123" }
  // - Inspect state: { loading: false, user: null }
  // - Edit values and see instant updates
  
  return <Text>{user?.name}</Text>;
}

Performance Profiling

import { Profiler } from 'react';

function onRenderCallback(
  id: string,
  phase: 'mount' | 'update',
  actualDuration: number,
  baseDuration: number,
  startTime: number,
  commitTime: number
) {
  console.log(`${id} ${phase} took ${actualDuration}ms`);
}

export default function App() {
  return (
    <Profiler id="App" onRender={onRenderCallback}>
      <Navigation />
    </Profiler>
  );
}

Network Debugging

Network Inspector

expo-dev-client includes a built-in network inspector.
1

Enable network inspection

Open dev menu and select “Network Inspector”.
2

View requests

See all HTTP requests:
  • URL and method
  • Status code and timing
  • Headers and body
  • Response data
3

Inspect request details

// All fetch requests are automatically captured
const response = await fetch('https://api.example.com/users');
const data = await response.json();

// View in Network Inspector:
// - Request headers
// - Response headers  
// - Response body
// - Timing information

Network Debugging Tools

utils/api.ts
// Add request interceptor for debugging
const originalFetch = global.fetch;

global.fetch = async (input, init) => {
  const start = Date.now();
  console.log('→', init?.method || 'GET', input);
  
  try {
    const response = await originalFetch(input, init);
    const duration = Date.now() - start;
    
    console.log('←', response.status, input, `${duration}ms`);
    return response;
  } catch (error) {
    console.error('✗', input, error);
    throw error;
  }
};

Proxying Requests

For debugging with tools like Charles or Proxyman:
app.json
{
  "expo": {
    "packagerOpts": {
      "dev": true
    },
    "ios": {
      "networkInspector": true
    }
  }
}
# Set proxy on device
# iOS: Settings > WiFi > Configure Proxy > Manual
# Android: Settings > WiFi > Modify Network > Proxy > Manual

Element Inspector

Visually inspect and debug UI elements.
1

Enable inspector

Shake device or press:
  • iOS: Cmd+D
  • Android: Cmd+M
Select “Show Element Inspector”.
2

Inspect elements

Tap any UI element to see:
  • Component name
  • Props
  • Styles
  • Layout dimensions
  • Position in hierarchy
3

Navigate hierarchy

Use breadcrumbs to navigate up the component tree.

Style Debugging

app/components/Card.tsx
import { StyleSheet } from 'react-native';

export function Card({ children }: { children: React.ReactNode }) {
  return (
    <View style={styles.container}>
      {children}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    padding: 16,
    backgroundColor: '#fff',
    borderRadius: 8,
    // In inspector, see computed styles:
    // - padding: 16
    // - backgroundColor: "rgb(255, 255, 255)"
    // - borderRadius: 8
  },
});

Native Debugging

iOS with Xcode

Debug native iOS code and crashes.
1

Open in Xcode

# Open workspace
open ios/YourApp.xcworkspace

# Or from CLI
npx expo run:ios
2

Set native breakpoints

ios/YourApp/AppDelegate.swift
import ExpoModulesCore

@UIApplicationMain
class AppDelegate: ExpoAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    // Set breakpoint here
    print("App launched")
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}
3

View console output

In Xcode:
  • View > Debug Area > Show Debug Area (Cmd+Shift+Y)
  • See native logs and crashes
4

Debug memory issues

# Run with memory debugging
Product > Scheme > Edit Scheme > Run > Diagnostics
# Enable: Address Sanitizer, Zombie Objects

Android with Android Studio

Debug native Android code.
1

Open in Android Studio

# Open project
studio android/

# Or from CLI
npx expo run:android
2

Attach debugger

  1. Run > Attach Debugger to Android Process
  2. Select your app process
  3. Choose “Java” debugger
3

Set native breakpoints

android/app/src/main/java/com/yourapp/MainApplication.kt
import expo.modules.ApplicationLifecycleDispatcher

class MainApplication : Application() {
  override fun onCreate() {
    super.onCreate()
    // Set breakpoint here
    Log.d("YourApp", "App started")
  }
}
4

View Logcat

View > Tool Windows > LogcatFilter by your package:
package:com.yourapp

Debugging Native Modules

When working with Expo modules:
ios/Modules/MyModule/MyModule.swift
import ExpoModulesCore

public class MyModule: Module {
  public func definition() -> ModuleDefinition {
    Name("MyModule")
    
    Function("doSomething") { (value: String) -> String in
      // Set breakpoint here to debug
      print("Native method called with: \(value)")
      return "Result: \(value)"
    }
  }
}
android/src/main/java/expo/modules/mymodule/MyModule.kt
package expo.modules.mymodule

import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition

class MyModule : Module() {
  override fun definition() = ModuleDefinition {
    Name("MyModule")
    
    Function("doSomething") { value: String ->
      // Set breakpoint here to debug
      Log.d("MyModule", "Native method called with: $value")
      "Result: $value"
    }
  }
}

Performance Debugging

Performance Monitor

Enable the performance overlay:
# In dev menu
Show Performance Monitor
Displays:
  • JavaScript frame rate
  • UI frame rate
  • Memory usage
  • Views count

Profiling with Hermes

For better performance, use Hermes:
app.json
{
  "expo": {
    "jsEngine": "hermes"
  }
}
Profile with Chrome DevTools:
# Start with profiling
npx expo start --dev-client

# Open chrome://inspect
# Click "inspect" next to Hermes
# Go to "Performance" tab
# Record and analyze

React Native Performance

import { InteractionManager } from 'react-native';

// Defer expensive operations
InteractionManager.runAfterInteractions(() => {
  // Expensive operation after animations
  processLargeDataset();
});

// Track slow renders
const onRender = (id, phase, actualDuration) => {
  if (actualDuration > 16) { // Slower than 60fps
    console.warn(`Slow render: ${id} took ${actualDuration}ms`);
  }
};

Troubleshooting

Debugger Won’t Connect

# Check Metro is running
lsof -i :8081

# Restart Metro
npx expo start --dev-client --clear

# Try different host
npx expo start --dev-client --host tunnel

Source Maps Not Working

# Clear Metro cache
npx expo start --clear

# Rebuild
npx expo run:ios --clean
npx expo run:android --clean

Performance Issues in Debug Mode

Debug mode is slower than production:
# Test performance in release mode
npx expo run:ios --configuration Release
npx expo run:android --variant release

Xcode: Process Launch Failed

# Clean build folder
cd ios
xcodebuild clean
rm -rf ~/Library/Developer/Xcode/DerivedData

# Rebuild
cd ..
npx expo run:ios --clean

Next Steps

DevTools

Explore Expo DevTools and plugins

Inspector

Use the element inspector

Error Handling

Handle errors and crashes

Testing

Set up automated testing