React Native with GitHub Codespaces
GitHub Codespaces provides a powerful, cloud-based development environment accessible directly from your browser. This makes it an excellent choice for React Native development, especially for Android builds, as it eliminates complex local setup, reduces resource usage on your machine, and ensures a consistent environment across devices.
Prerequisites
Whether setting up manually or using a Dev Container, your environment will need:
- Node.js: Multiple versions are usually available (e.g., v18, v20). The Dev Container approach explicitly installs a version (e.g., LTS). For manual setup, you might need to switch versions using tools like
nvm
. - Java Development Kit (JDK): Required for Android development (e.g., OpenJDK 17 or 21). The Dev Container explicitly installs a version (e.g., 17). For manual setup, it’s usually pre-installed in the base Codespace image.
You have two main options for setting up the Android-specific tools (SDK, build tools, NDK): automating it with a Dev Container configuration or setting it up manually within the Codespace.
Option 1: Automated Setup with Dev Container (Recommended)
This approach uses GitHub Codespaces’ built-in container configuration to automatically set up the necessary Android environment when your Codespace is created or rebuilt. This is generally faster and less error-prone than manual setup.
-
Create the Dev Container Configuration Files
In the root of your project repository, create a folder named
.devcontainer
. Inside this folder, create two files:a.
devcontainer.json
: This file defines the Codespace environment, including the base image, features (like Node.js and Java), and commands to run after the container is created..devcontainer/devcontainer.json {"name": "React Native Android Dev Environment","image": "mcr.microsoft.com/devcontainers/base:ubuntu", // Base Ubuntu image"features": {// Installs specified Java version"ghcr.io/devcontainers/features/java:1": {"version": "17" // Or "21", "11", etc., matching project needs// "jdkDistro": "ms" // Optional: specify distribution},// Installs Node.js (LTS version)"ghcr.io/devcontainers/features/node:1": {"version": "lts"}// Add more features if needed (e.g., Docker, databases)},// Optional: Define minimum host machine requirements"hostRequirements": {"cpus": 4,"memory": "16gb","storage": "32gb"},"customizations": {"vscode": {"extensions": [// Add useful VS Code extensions"dbaeumer.vscode-eslint","esbenp.prettier-vscode","msjsdiag.vscode-react-native","eamodio.gitlens"]}},// Command to run *after* the container is created and features are installed// Runs the setup script and then installs project dependencies"postCreateCommand": "bash .devcontainer/setup.sh && npm install","remoteEnv": {// Example: Define environment variables needed by your app/build// "MY_API_KEY": "your-key-here" // Use Codespace Secrets for sensitive values}}b.
setup.sh
: This script, referenced inpostCreateCommand
, handles the download, installation, and configuration of the Android SDK components..devcontainer/setup.sh #!/bin/bashset -e # Exit immediately if a command exits with a non-zero status.# Store the original working directory (should be the workspace root)ORIGINAL_DIR=$(pwd)echo "--- Starting Android SDK setup in ${ORIGINAL_DIR} ---"# 1. Define SDK installation directory within the container's homeSDK_DIR="/home/vscode/android-sdk"echo "Target SDK Installation directory: ${SDK_DIR}"mkdir -p "${SDK_DIR}" # Ensure SDK root directory exists# Use a temporary directory for download and unzipTEMP_DIR=$(mktemp -d)echo "Working temporarily in ${TEMP_DIR}"cd "${TEMP_DIR}" # Change into temp dir# 2. Download the latest command-line tools# Note: Check for the latest URL/version if this script fails years laterCMDLINE_TOOLS_URL="https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip"echo "Downloading command-line tools from ${CMDLINE_TOOLS_URL}..."curl -fsSL -o commandlinetools.zip "${CMDLINE_TOOLS_URL}"# 3. Unzip the toolsecho "Unzipping tools..."unzip -q commandlinetools.zip# 4. Prepare final SDK structure directorymkdir -p "${SDK_DIR}/cmdline-tools"# 5. Move the unzipped 'cmdline-tools' directory to the final locationecho "Moving tools to final destination: ${SDK_DIR}/cmdline-tools/latest"# The unzipped folder is named 'cmdline-tools', move its contentsmv "${TEMP_DIR}/cmdline-tools" "${SDK_DIR}/cmdline-tools/latest"# 6. Clean up the temporary directoryecho "Cleaning up temporary directory ${TEMP_DIR}..."# IMPORTANT: Change back to the original directory *before* removing temp dircd "${ORIGINAL_DIR}"rm -rf "${TEMP_DIR}"echo "Returned to ${ORIGINAL_DIR}"# 7. Set Environment Variables for *this script's execution*# These help sdkmanager run correctly within this script.# Gradle primarily uses the local.properties file (created later).echo "Setting temporary environment variables for SDK Manager..."export ANDROID_SDK_ROOT="${SDK_DIR}"export ANDROID_HOME="${SDK_DIR}" # Some tools might still prefer ANDROID_HOMEexport PATH="${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin:${ANDROID_SDK_ROOT}/platform-tools:${PATH}"echo "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}"echo "Updated PATH includes SDK tools temporarily"# Define the path to sdkmanagerSDKMANAGER="${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager"# 8. Verify sdkmanagerecho "Verifying sdkmanager...""${SDKMANAGER}" --version# 9. Accept licenses automaticallyecho "Accepting licenses..."yes | "${SDKMANAGER}" --licenses > /dev/null# 10. Install required SDK components# Adjust versions based on your React Native project requirementsPLATFORMS_VERSION="android-34"BUILD_TOOLS_VERSION="34.0.0"# Check React Native docs for the recommended NDK version for your RN versionNDK_VERSION="26.1.10909125"echo "Installing SDK components: platform-tools, platforms;${PLATFORMS_VERSION}, build-tools;${BUILD_TOOLS_VERSION}, ndk;${NDK_VERSION}""${SDKMANAGER}" "platform-tools" "platforms;${PLATFORMS_VERSION}" "build-tools;${BUILD_TOOLS_VERSION}" "ndk;${NDK_VERSION}"# 11. Create local.properties for Gradle# Assumes an 'android' directory exists at the project root.# This tells Gradle where to find the SDK.echo "Creating/Updating android/local.properties with SDK path..."if [ -d "android" ]; thenecho "sdk.dir=${SDK_DIR}" > android/local.propertiesecho "Created android/local.properties"elseecho "Warning: 'android' directory not found at project root. Skipping creation of local.properties."echo "You may need to create this file manually inside your 'android' directory: echo 'sdk.dir=${SDK_DIR}' > android/local.properties"fiecho "--- Android SDK setup script finished successfully ---"# Optional: Add a reminder about .gitignoreecho "*** REMINDER: Ensure 'local.properties' is listed in your /android/.gitignore file! ***" -
Commit and Push
Commit the
.devcontainer
folder and its contents (devcontainer.json
,setup.sh
) to your GitHub repository. -
Rebuild or Create the Codespace
If you already have a Codespace open for this repository, you’ll need to rebuild it for the changes to take effect. Look for a command palette option (Ctrl+Shift+P or Cmd+Shift+P) like “Codespaces: Rebuild Container”. If you’re creating a new Codespace, it will automatically use this configuration.
During the build, you’ll see logs indicating that the
postCreateCommand
is running thesetup.sh
script andnpm install
. -
Verify Setup
Once the Codespace is ready, the Android SDK should be installed in
/home/vscode/android-sdk
, and theandroid/local.properties
file should point to it. You can now proceed directly to the “Developing Your App” section below, skipping Option 2 (Manual Setup). Note that while thesetup.sh
exports environment variables likeANDROID_HOME
, they might not be automatically present in new terminal sessions you open later. However, thelocal.properties
file ensures Gradle can find the SDK, which is the primary requirement for building.
Option 2: Manual Android Environment Setup
If you prefer not to use the Dev Container automation or need more granular control, you can set up the Android SDK manually within an existing Codespace. Note that you will need to repeat these steps if you create a fresh Codespace without the automated setup.
-
Install Android Command Line Tools
Create a directory, download, extract, and organize the tools:
Terminal window # Create directory and navigate into itmkdir -p ~/android-sdk && cd ~/android-sdk# Download the latest command line tools (check for updated URLs if needed)# URL might change, verify at https://developer.android.com/tools/releases/platform-toolswget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip# Unzip and structure the tools correctlyunzip commandlinetools-linux-*.zipmkdir -p cmdline-tools/latestmv cmdline-tools/* cmdline-tools/latest/ 2>/dev/null || true # Handles potential existing filesrm commandlinetools-linux-*.zip # Clean up downloaded zip# Return to your project directory (adjust path if needed)cd ~/workspaces/* # Or your specific project path -
Configure Environment Variables
Set the
ANDROID_HOME
variable and add the SDK tools to yourPATH
so they can be found by the system and React Native CLI. You also need to createlocal.properties
for Gradle.Terminal window # Add environment variables to your shell configuration file (e.g., .bashrc or .zshrc)echo '# Android SDK' >> ~/.bashrcecho 'export ANDROID_HOME=$HOME/android-sdk' >> ~/.bashrcecho 'export ANDROID_SDK_ROOT=$HOME/android-sdk' >> ~/.bashrc # Some tools use thisecho 'export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools' >> ~/.bashrc# Apply the changes to your current sessionsource ~/.bashrc# Create local.properties for Gradle (run from project root)if [ -d "android" ]; thenecho "sdk.dir=$HOME/android-sdk" > android/local.propertiesecho "Created/Updated android/local.properties"echo "*** REMINDER: Ensure 'local.properties' is listed in your /android/.gitignore file! ***"elseecho "Warning: 'android' directory not found. Cannot create local.properties."fi -
Install SDK Components
Use the
sdkmanager
tool (now in your PATH) to accept licenses and install the required Android SDK platforms, build tools, and other necessary components.Terminal window # Accept all SDK licensesyes | sdkmanager --licenses# Install required components (adjust versions as needed for your project)# Check React Native docs for recommended NDK versionsdkmanager \"platform-tools" \"platforms;android-34" \"build-tools;34.0.0" \"ndk;26.1.10909125"
Developing Your App
Whether you used the automated or manual setup, you can now work on your React Native project.
Initializing a New Project
If starting fresh, use the React Native CLI:
# Replace YourProjectName with your desired project namenpx @react-native-community/cli init YourProjectNamecd YourProjectName# If using manual setup & just initialized, create local.properties now:# echo "sdk.dir=$HOME/android-sdk" > android/local.properties
Building for Debug
-
Install Dependencies (if not already done by
postCreateCommand
in Dev Container)Navigate to your project’s root directory and run:
Terminal window npm install# or yarn install -
Prepare and Build Android Debug APK
Terminal window cd android# Ensure gradlew script is executable (NEEDED regardless of setup method)chmod +x gradlew./gradlew clean # Optional: Clean previous builds./gradlew assembleDebug # Build the debug APKThe debug APK will be located at
android/app/build/outputs/apk/debug/app-debug.apk
. You can download this file via the Codespaces file explorer (usually in the left sidebar under the “Explorer” tab) to install on a physical device or an emulator. Right-click the file and choose “Download…”.
Running and Debugging on a Device
-
Start the Metro Bundler
In your project’s root directory, start the Metro bundler which serves your JavaScript code:
Terminal window npx react-native start -
Forward the Metro Port (8081)
GitHub Codespaces should automatically detect port 8081 being used. Go to the Ports tab in VS Code (usually at the bottom panel next to Terminal, Problems, etc.). Find port 8081, right-click it, set Port Visibility to Public, and copy the Forwarded Address. It will look something like
https://your-codespace-name-random-string-8081.app.github.dev
. -
Connect Your Physical Android Device
- Install the
app-debug.apk
(downloaded previously) on your device. - Ensure your device is connected to the same network as your computer (though technically it just needs internet access to reach the public forwarded URL).
- Open the app.
- Open the Developer Menu (usually by shaking the device or running
adb shell input keyevent 82
if connected via ADB - though ADB connection to Codespaces is complex and usually not the primary method). - Go to Settings (or Dev Settings) > Debug server host & port for device.
- Enter the Forwarded Address from Step 2, removing the
https://
prefix but keeping the port number (e.g.,your-codespace-name-random-string-8081.app.github.dev
). - Go back and select Reload from the Developer Menu. The app should now connect to the Metro server running in your Codespace and load the bundle.
- Install the
Building for Release
To create a release build suitable for distribution (e.g., Google Play Store), you need to sign your Android app. These steps are manual and required regardless of whether you used the Dev Container or manual SDK setup.
-
Generate a Signing Key
Use
keytool
(part of the JDK installed earlier) to generate a private signing key. Store this keystore file securely and back it up! Do not commit the keystore file to Git.Terminal window # Navigate to the android/app directory firstcd android/app# Generate the keystorekeytool -genkeypair -v -keystore release.keystore -alias your-key-alias -keyalg RSA -keysize 2048 -validity 10000You will be prompted to create passwords for the keystore and the key. Remember these passwords. Replace
your-key-alias
with a unique name for your key. -
Configure Gradle for Signing
Edit your
android/app/build.gradle
file to read the credentials fromkeystore.properties
(or environment variables/secrets) and set up the release signing configuration.android/app/build.gradle ...// Add these lines near the top, outside the android { ... } blockdef keystorePropertiesFile = rootProject.file("app/keystore.properties")def keystoreProperties = new Properties()if (keystorePropertiesFile.exists()) {keystoreProperties.load(new FileInputStream(keystorePropertiesFile))}// You could alternatively load from environment variables hereandroid {...defaultConfig { ... }signingConfigs {debug {// Default debug signing configstoreFile file('debug.keystore')storePassword 'android'keyAlias 'androiddebugkey'keyPassword 'android'}release {// Read from keystore.properties if it existsif (keystorePropertiesFile.exists() && keystoreProperties['storePassword'] != null) {storeFile file(keystoreProperties['storeFile'])storePassword keystoreProperties['storePassword']keyAlias keystoreProperties['keyAlias']keyPassword keystoreProperties['keyPassword']} else {// Optional: Fallback, warning, or fail build if properties missingprintln("Warning: Release keystore properties not found in app/keystore.properties. Release signing disabled.")// Or you might want to explicitly disable release signing here or throw an error// signingConfig null // Example: disable signing if props missing}}}buildTypes {debug {signingConfig signingConfigs.debug...}release {// Apply the release signing configurationsigningConfig signingConfigs.release// Enable code shrinking, obfuscation, and resource optimizationminifyEnabled trueshrinkResources trueproguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"...}}...} -
Build the Release APK
Navigate back to the
android
directory and run theassembleRelease
Gradle task:Terminal window cd .. # Go back to android/ directory if you were in android/app/./gradlew clean# This task generates a signed APK if release signing is configured./gradlew assembleReleaseThe signed release APK(s) will be generated in
android/app/build/outputs/apk/release/
. Download these for distribution.
Optimizing APK Size (Optional: ABI Splitting)
To reduce the app’s download size for users, you can generate separate APKs for different CPU architectures (Application Binary Interfaces - ABIs). Add the following splits
block inside the android { ... }
block in android/app/build.gradle
:
android { // ... other configurations like compileSdkVersion, defaultConfig, signingConfigs, buildTypes ...
splits { abi { enable true // Enable ABI splitting reset() // Clear any existing filters // Specify architectures to build for (common choices shown) include "armeabi-v7a", "arm64-v8a" // Common for 32-bit and 64-bit ARM phones // include "x86", "x86_64" // Often needed for emulators or specific devices like Chromebooks universalApk false // Set to true to ALSO build one large APK containing all ABIs // Usually set to false when uploading splits to Google Play } }
// If using Hermes and ProGuard, ensure NDK is available (handled by setup scripts) // ... rest of android block ...}