This guide covers React Native development using the Devbox React Native plugin, which composes the Android and iOS plugins to provide a complete cross-platform mobile development environment.
The React Native plugin is a composition layer that combines the Android and iOS plugins to deliver:
- Cross-platform device management - Inherits Android emulator and iOS simulator management from both platform plugins
- Metro bundler orchestration - Manages Metro instances with dynamic port allocation and isolated state
- Hot reload development - Fast iteration with Debug builds and instant code updates
- Automated testing - E2E test suites with Release builds and cleanup
- Web bundling support - Optional web target for React Native Web projects
- Process isolation - Multiple Metro instances can run in parallel for testing different platform versions
The plugin does not modify React Native itself. It provides the native platform tooling (Android SDK, Xcode, simulators, emulators) and development workflow automation.
A React Native project with package.json and platform-specific code in android/ and ios/ directories. Install devbox if you have not already -- it handles downloading all required tools (Node.js, Android SDK, build tooling, etc.) automatically.
For iOS development, macOS with Xcode installed is required.
Devbox plugins are included via URL in the "include" field of devbox.json, not through devbox add. The plugin URL points to this repository and specifies which plugin directory to use.
Include the React Native plugin in your devbox.json:
{
"include": ["github:segment-integrations/devbox-plugins?dir=plugins/react-native"],
"packages": [
"nodejs@20",
"watchman@latest",
"jdk17@latest",
"gradle@latest"
],
"env": {
"ANDROID_APP_ID": "com.example.app",
"ANDROID_APP_APK": "android/app/build/outputs/apk/debug/app-debug.apk",
"IOS_APP_ARTIFACT": ".devbox/virtenv/ios/DerivedData/Build/Products/Debug-iphonesimulator/MyApp.app"
}
}The React Native plugin inherits configuration from both Android and iOS plugins. Configure platform-specific settings via environment variables in devbox.json.
Common React Native settings:
{
"env": {
"ANDROID_DEFAULT_DEVICE": "max",
"IOS_DEFAULT_DEVICE": "max",
"ANDROID_DEVICES": "min,max",
"IOS_DEVICES": "min,max",
"WEB_BUILD_PATH": "web/build"
}
}Android configuration - See Android Guide for details:
ANDROID_DEFAULT_DEVICE- Default emulator (e.g., "max", "min", "pixel_api30")ANDROID_DEVICES- Comma-separated devices to evaluate (empty = all)ANDROID_APP_ID- Android package identifierANDROID_APP_APK- APK path/glob after buildANDROID_COMPILE_SDK- Compile SDK versionANDROID_TARGET_SDK- Target SDK version
iOS configuration - See iOS Guide for details:
IOS_DEFAULT_DEVICE- Default simulator (e.g., "max", "min", "iphone15")IOS_DEVICES- Comma-separated devices to evaluate (empty = all)IOS_APP_ARTIFACT- App path/glob after build (empty = auto-detect)IOS_DOWNLOAD_RUNTIME- Auto-download missing runtimes (1=yes, 0=no)
Install Node.js dependencies and native CocoaPods:
# Install npm packages
devbox run install
# iOS: Install CocoaPods (first time only)
cd ios && pod install --repo-updateReact Native development with Devbox supports two primary workflows: interactive development with hot reload and automated E2E testing.
Development mode prioritizes fast iteration with hot reload enabled. It uses Debug builds for faster compilation and reuses existing emulators/simulators to avoid startup overhead.
The commands below (start:android, start:ios, start:web) are user-defined scripts from the example project. See the example project's devbox.json for how they are defined. You will need to add similar scripts to your own devbox.json.
Start development for Android:
devbox run start:androidStart development for iOS:
devbox run start:iosStart development for Web:
devbox run start:webWhat happens:
- Allocates a unique Metro port (8091-8199 range)
- Installs Node.js dependencies
- Builds the app (Debug configuration)
- Starts or reuses emulator/simulator
- Starts Metro bundler with hot reload
- Deploys the app
- Leaves Metro and emulator/simulator running
Hot reload workflow:
- Edit code in your editor (e.g.,
App.tsx) - Save the file
- Changes appear automatically in the running app (powered by Fast Refresh)
Manual reload:
- Android: Press
Rtwice orCtrl/Cmd + Mto open Dev Menu - iOS: Press
Rin simulator - Both: Shake device to open Dev Menu
Enable TUI for interactive monitoring:
DEVBOX_TUI=true devbox run start:android
DEVBOX_TUI=true devbox run start:iosTUI mode provides a terminal interface to monitor all processes (build, emulator, Metro, deploy) in real-time.
Build platform-specific artifacts without running them. These build commands are user-defined scripts from the example project -- you will need to define your own build scripts in your devbox.json that match your project's build process.
# Build for Android (Debug)
devbox run build:android
# Build for iOS (Debug)
devbox run build:ios
# Build for Web
devbox run build:web
# Build all platforms
devbox run buildBuilds are cached. Subsequent builds are incremental and faster.
The React Native plugin inherits device management from both platform plugins. Devices are defined in JSON files and automatically synced before emulator/simulator startup.
List available devices:
# Android devices
devbox run --pure android.sh devices list
# iOS devices
devbox run --pure ios.sh devices listCreate custom devices:
# Android - specify API level and device profile
devbox run --pure android.sh devices create pixel_api30 \
--api 30 \
--device pixel \
--tag google_apis
# iOS - specify simulator runtime
devbox run --pure ios.sh devices create iphone14 \
--runtime 16.4
# Regenerate lock files after changes
devbox run --pure android.sh devices eval
devbox run --pure ios.sh devices evalDevice definitions:
Device JSON files are stored in the devices directory within your devbox.d folder. The exact path depends on how the plugin is included (local vs GitHub URL). Use the devices list command to see the configured path.
Default devices:
Device definitions include min.json and max.json representing the minimum and maximum supported platform versions. Configure which device to use with ANDROID_DEFAULT_DEVICE and IOS_DEFAULT_DEVICE.
Override default device at runtime:
# Start specific emulator
devbox run start:emu pixel_api30
# Start specific simulator
devbox run start:sim iphone14For complete device management documentation, see:
Metro is the JavaScript build tool for React Native. The plugin manages Metro instances with isolated state and dynamic port allocation.
Automatic Metro management (recommended):
Metro is automatically started and stopped by development and test commands:
devbox run start:android # Starts Metro automatically
devbox run start:ios # Starts Metro automaticallyManual Metro control (advanced):
The start:metro and stop:metro scripts below are user-defined wrappers from the example project. The plugin provides the metro.sh CLI directly.
# Start Metro for specific suite (user-defined wrapper)
devbox run start:metro android
devbox run start:metro ios
# Stop Metro for specific suite (user-defined wrapper)
devbox run stop:metro android
devbox run stop:metro ios
# Plugin-provided CLI commands
metro.sh status android
metro.sh status ios
# Health check (exit code only, for readiness probes)
metro.sh health android android
metro.sh health ios ios
# Clean Metro state files
metro.sh clean android
metro.sh clean iosKey features:
- Dynamic port allocation - Allocates ports in range 8091-8199 to avoid conflicts
- Unique suite names - Isolate Metro instances (android, ios, web, all)
- Parallel test support - Run multiple Metro instances simultaneously
- Automatic cleanup - Process-compose handles lifecycle
- Environment isolation - Each suite gets unique environment files
Metro state files:
Located in .devbox/virtenv/react-native/metro/:
port-{suite}.txt- Allocated port numberenv-{suite}.sh- Environment variables (sourced by deploy scripts)- Cache files managed automatically
Metro environment variables:
When Metro starts, it exports:
METRO_PORT- Allocated port number- Cache directory paths
Deploy scripts source env-{suite}.sh to ensure React Native uses the correct Metro port.
The example project demonstrates automated E2E testing with Release builds and cleanup. The test commands shown below are user-defined scripts from the example project. You will need to define your own test scripts in your devbox.json.
# Test iOS only (fast - skips Android SDK)
devbox run test:e2e:ios
# Test Android only (fast - skips iOS setup)
devbox run test:e2e:android
# Test both platforms in parallel
devbox run test:e2e:all
# Test web bundle
devbox run test:e2e:webE2E test behavior:
- Automatically runs with
--pureflag (isolated, reproducible) - Uses Release builds for production-like behavior
- Starts fresh emulator/simulator from scratch
- Automatic cleanup after tests complete
- No TUI (automated, non-interactive)
- Summary reports with test results and log locations
Test logs:
Logs are written to reports/ directory:
- iOS E2E:
reports/react-native-ios-e2e-logs/ - Android E2E:
reports/react-native-android-e2e-logs/ - Web E2E:
reports/react-native-web-e2e-logs/ - Dev mode:
reports/react-native-{platform}-dev-logs/
CI Mode (--pure) - Default for E2E Tests
Purpose: Deterministic, reproducible test runs for continuous integration.
# E2E commands automatically use --pure
devbox run test:e2e:ios
devbox run test:e2e:androidBehavior:
- Always starts fresh emulator/simulator from scratch
- Clean state with no cached data or previous state
- Automatic cleanup (stops and deletes devices after completion)
- Isolated (each test run is completely independent)
Development Mode - Default for start:* Commands
Purpose: Fast iteration during local development.
devbox run start:android
devbox run start:iosBehavior:
- Reuses existing emulator/simulator if available
- Starts device only if not already running
- No cleanup (leaves device running after completion)
- Fast iteration with no startup/shutdown overhead
Development mode uses Debug builds. E2E tests use Release builds.
Override with environment variables:
# Force Debug build in E2E test (faster compilation)
IOS_BUILD_CONFIG=Debug devbox run test:e2e:ios
# Force Release build in dev mode (slower but production-like)
BUILD_CONFIG=Release devbox run start:androidWhen testing a single platform, skip the unused platform to speed up initialization:
# iOS tests only (skips Android SDK evaluation)
devbox run test:e2e:ios
# Android tests only (skips iOS setup)
devbox run test:e2e:androidThe test commands automatically set optimization flags:
ANDROID_SKIP_SETUP=1- Skip Android SDK Nix flake evaluation (iOS tests)IOS_SKIP_SETUP=1- Skip iOS environment setup (Android tests)
Important: When using --pure mode manually, pass environment variables with -e flag:
# Correct way to skip Android SDK in pure mode
devbox run --pure -e ANDROID_SKIP_SETUP=1 test:e2e:ios
# Incorrect - env var gets reset to default
ANDROID_SKIP_SETUP=1 devbox run --pure test:e2e:iosTest multiple platform versions in parallel by creating separate test suite files with unique suite names.
Example: Test Android API 21 and API 35 simultaneously
Create test-suite-android-api21.yaml:
environment:
- "ANDROID_DEFAULT_DEVICE=min"
processes:
allocate-metro-port:
command: |
. ${REACT_NATIVE_VIRTENV}/scripts/lib/lib.sh
metro_port=$(rn_allocate_metro_port "android-api21")
rn_save_metro_env "android-api21" "$metro_port"
metro-bundler:
command: "metro.sh start android-api21"
cleanup:
command: "metro.sh stop android-api21"Create test-suite-android-api35.yaml:
environment:
- "ANDROID_DEFAULT_DEVICE=max"
processes:
allocate-metro-port:
command: |
. ${REACT_NATIVE_VIRTENV}/scripts/lib/lib.sh
metro_port=$(rn_allocate_metro_port "android-api35")
rn_save_metro_env "android-api35" "$metro_port"
metro-bundler:
command: "metro.sh start android-api35"
cleanup:
command: "metro.sh stop android-api35"Run in parallel:
process-compose -f test-suite-android-api21.yaml &
process-compose -f test-suite-android-api35.yaml &
waitKey requirement: Use unique suite names for:
rn_allocate_metro_portcallsmetro.sh start/stopcommands- Environment file names
This ensures each test gets its own Metro instance on a unique port with isolated state.
If you see "Metro port already in use" errors:
# Check what's using the port
lsof -ti:8081
# Stop Metro for specific suite
metro.sh stop android
metro.sh stop ios
# Clean all Metro state
metro.sh clean android
metro.sh clean iosAndroid:
# Check emulator status
adb devices
# View emulator logs
tail -f reports/react-native-android-dev-logs/*.log
# Stop and restart
devbox run stop:emu
devbox run start:emuiOS:
# Check simulator status
xcrun simctl list devices
# View simulator logs
tail -f reports/react-native-ios-dev-logs/*.log
# Stop and restart
devbox run stop:sim
devbox run start:sim- Check Metro is running:
metro.sh status android - Check Metro logs for errors
- Force reload: Press
Rtwice (Android) orRonce (iOS) - Restart Metro:
devbox run stop:metro android && devbox run start:metro android
Android Gradle errors:
# Clean build
cd android && ./gradlew clean assembleDebugiOS Xcode errors:
# Clean derived data
rm -rf .devbox/virtenv/ios/DerivedData
# Reinstall CocoaPods
cd ios && pod install --repo-update
# Rebuild
devbox run build:iosFor platform-specific troubleshooting, see:
Enable debug logging to diagnose issues:
# React Native debug
DEBUG=1 devbox run start:android
# Android debug
ANDROID_DEBUG=1 devbox run start:android
# iOS debug
IOS_DEBUG=1 devbox run start:ios
# Combined
DEBUG=1 ANDROID_DEBUG=1 devbox run start:androidView all process logs:
# Development logs
ls -la reports/react-native-android-dev-logs/
ls -la reports/react-native-ios-dev-logs/
# E2E test logs
ls -la reports/react-native-android-e2e-logs/
ls -la reports/react-native-ios-e2e-logs/React Native settings:
WEB_BUILD_PATH- Web bundle output directory (default: "web/build")
Android settings (see Android Reference):
ANDROID_DEFAULT_DEVICE- Default emulatorANDROID_DEVICES- Devices to evaluate (comma-separated, empty = all)ANDROID_APP_ID- Android package identifierANDROID_APP_APK- APK path/globANDROID_COMPILE_SDK- Compile SDK versionANDROID_TARGET_SDK- Target SDK versionANDROID_BUILD_TOOLS_VERSION- Build tools versionANDROID_SKIP_SETUP- Skip SDK downloads (1=skip, 0=evaluate)
iOS settings (see iOS Reference):
IOS_DEFAULT_DEVICE- Default simulatorIOS_DEVICES- Devices to evaluate (comma-separated, empty = all)IOS_APP_ARTIFACT- App bundle path/glob (empty = auto-detect)IOS_DOWNLOAD_RUNTIME- Auto-download runtimes (1=yes, 0=no)IOS_SKIP_SETUP- Skip iOS setup (1=skip, 0=setup)
These commands are provided by the Android, iOS, and React Native plugins and are available automatically when the plugin is included.
Emulator/Simulator management (from Android and iOS plugins):
devbox run start:emu [device]- Start Android emulatordevbox run stop:emu- Stop Android emulatordevbox run start:sim [device]- Start iOS simulatordevbox run stop:sim- Stop iOS simulator
Device management CLI (from Android and iOS plugins):
android.sh devices list- List Android devicesandroid.sh devices create- Create Android deviceandroid.sh devices sync- Sync AVDs with definitionsios.sh devices list- List iOS devicesios.sh devices create- Create iOS deviceios.sh devices sync- Sync simulators with definitions
Metro CLI (from React Native plugin):
metro.sh start [suite]- Start Metro bundlermetro.sh stop [suite]- Stop Metro bundlermetro.sh status [suite]- Check Metro statusmetro.sh health [suite] [platform]- Health check (exit code)metro.sh clean [suite]- Clean Metro state files
Diagnostics (from all plugins):
devbox run doctor- Check environment healthdevbox run verify:setup- Verify environment is correctly configured
These commands are not provided by the plugins. They are defined in the example project's devbox.json and serve as a reference for how to build your own workflow scripts. You must add similar scripts to your own devbox.json to use them.
Development workflow:
devbox run start:android- Start Android development with hot reloaddevbox run start:ios- Start iOS development with hot reloaddevbox run start:web- Start web development with browserdevbox run start:metro [suite]- Start Metro bundler (wrapsmetro.sh start)devbox run stop:metro [suite]- Stop Metro bundler (wrapsmetro.sh stop)
Building:
devbox run build:android- Build Android APK (Debug)devbox run build:ios- Build iOS app (Debug)devbox run build:web- Build web bundledevbox run build- Build all platforms
Testing:
devbox run test:e2e:android- Android E2E tests (--pure)devbox run test:e2e:ios- iOS E2E tests (--pure)devbox run test:e2e:all- Both platforms in paralleldevbox run test:e2e:web- Web bundle testsdevbox run test- Run Jest tests
Configuration:
devbox.json- Project configurationdevbox.d/- Plugin configuration directory containing device definitions for Android and iOS (exact subdirectory structure depends on how plugins are included)
Generated files:
.devbox/virtenv/react-native/metro/- Metro state filesport-{suite}.txt- Allocated Metro portenv-{suite}.sh- Metro environment variables
.devbox/virtenv/android/- Android runtime files.devbox/virtenv/ios/- iOS runtime files
Logs:
reports/react-native-{platform}-dev-logs/- Development logsreports/react-native-{platform}-e2e-logs/- E2E test logs
- Android Development Guide - Platform-specific Android details
- iOS Development Guide - Platform-specific iOS details
- Device Management Guide - Cross-platform device management
- Testing Guide - E2E testing patterns
- Troubleshooting Guide - Common issues and solutions
- React Native Plugin Reference - Complete API reference
- Android Plugin Reference - Android configuration options
- iOS Plugin Reference - iOS configuration options
/examples/react-native/- Complete React Native app with test suites/examples/android/- Minimal Android app/examples/ios/- Swift package example
For full environment setup with both platforms:
devbox shellThis provides a fully configured shell with Android SDK, iOS tooling, Metro, and all development tools ready to use.