Skip to content

Quick Start - React Native

This guide walks you through setting up a RunnerHub pipeline for React Native projects that target both iOS and Android. Build, test, code sign, and deploy your app with automated dependency caching and unified Node.js management.

Migration Notice

Already using platform: ios for React Native? Switch to platform: react-native to get:

  • Automatic Node.js provisioning (no more manual fnm install)
  • Android builds with the same app definition
  • Unified dual signing (Apple + Android) in one app
  • Metro bundler caching out of the box

Your existing platform: ios pipelines keep working — migration is opt-in.

Create .runnerhub/runnerhub.yml in your repository root with a multi-job pipeline for iOS and Android:

name: React Native Build
platform: react-native
environment:
node: lts
xcode: "16.4"
android_sdk: 34
triggers:
- push
- pull_request
jobs:
test:
name: Unit Tests
steps:
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
build-ios:
name: Build iOS
needs: [test]
steps:
- name: Install dependencies
run: npm install
- name: Install pods
run: |
cd ios
pod install
- name: Build iOS App
run: |
cd ios
xcodebuild -workspace App.xcworkspace -scheme App build
build-android:
name: Build Android
needs: [test]
steps:
- name: Install dependencies
run: npm install
- name: Build Android App
run: |
cd android
./gradlew assembleRelease

Replace App with your actual app name and adjust workspace/gradle paths as needed.

Step 2: Understand Environment Requirements

Section titled “Step 2: Understand Environment Requirements”

The environment block now includes:

  • node: lts — Automatically installs and activates the Node.js LTS version. Accepted values: specific versions like "20", "20.11", "20.11.1", or release channels "lts" and "latest"
  • xcode: "16.4" — Required for iOS builds
  • android_sdk: 34 — Required for Android builds
  • Optional: ruby: "3.2" — If using Bundler or CocoaPods with Ruby requirements

Update the YAML with your project specifics:

  • Package manager: Change npm install to yarn install if needed. To use pnpm, add a setup step: npm install -g pnpm (pnpm is not pre-installed on golden images; see line 242 for a complete example)
  • iOS workspace and scheme: Update to match your project
  • Android gradle tasks: Adjust assembleRelease as needed (e.g., bundleRelease for App Bundle)
  • Job structure: Keep the test job, or split iOS/Android into more granular jobs as needed
  1. Commit and push .runnerhub/runnerhub.yml to your repository
  2. Go to app.runnerhub.net and navigate to your app
  3. Watch your React Native build run in the dashboard

Here’s a more comprehensive pipeline including linting, code signing for both platforms, and artifact uploads:

name: React Native Build & Sign
platform: react-native
environment:
node: "20.11"
xcode: "16.4"
android_sdk: 34
triggers:
- push
- pull_request
jobs:
lint-and-test:
name: Lint & Test
steps:
- name: Install dependencies
run: npm install
- name: Lint
run: npm run lint
- name: Run tests
run: npm test
build-ios-signed:
name: Build & Sign iOS
needs: [lint-and-test]
steps:
- name: Install dependencies
run: npm install
- name: Install pods
run: |
cd ios
pod install
- name: Build iOS Release
run: |
cd ios
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Release \
-derivedDataPath build \
build
artifacts:
- ios/build/**/*.app
- ios/build/**/*.dSYM
build-android-signed:
name: Build & Sign Android
needs: [lint-and-test]
steps:
- name: Install dependencies
run: npm install
- name: Build Android Release
run: |
cd android
./gradlew bundleRelease
artifacts:
- android/app/build/outputs/bundle/release/**/*.aab

This example demonstrates:

  • Shared test job that both iOS and Android depend on
  • Conditional pipeline execution using triggers to gate to the main branch
  • Artifact collection for signed builds
  • Version-specific Node.js (20.11) for consistency

To make the signed builds run only on main, update triggers to:

triggers:
- event: push
branches: [main]
- event: pull_request
branches: [main]

If your project uses Yarn instead of npm:

name: React Native Build
platform: react-native
environment:
node: lts
xcode: "16.4"
android_sdk: 34
triggers:
- push
- pull_request
jobs:
build-ios:
name: Build iOS
steps:
- name: Install dependencies
run: yarn install
- name: Install pods
run: |
cd ios
pod install
- name: Build iOS
run: |
cd ios
xcodebuild -workspace App.xcworkspace -scheme App build
build-android:
name: Build Android
steps:
- name: Install dependencies
run: yarn install
- name: Build Android
run: |
cd android
./gradlew assembleRelease

Note: Yarn lockfile (yarn.lock) is automatically cached by RunnerHub. If you use pnpm, add a setup step to install it first: npm install -g pnpm, then use pnpm install (pnpm lockfile pnpm-lock.yaml is also cached automatically).

If your React Native app is in a subdirectory using a monorepo structure:

name: React Native Monorepo
platform: react-native
environment:
node: lts
xcode: "16.4"
android_sdk: 34
triggers:
- push
- pull_request
jobs:
build-ios:
name: Build iOS
steps:
- name: Install dependencies
run: npm install
working_directory: .
- name: Install pods
run: pod install
working_directory: apps/mobile/ios
- name: Build iOS
run: xcodebuild -workspace App.xcworkspace -scheme App build
working_directory: apps/mobile/ios
build-android:
name: Build Android
steps:
- name: Install dependencies
run: npm install
working_directory: .
- name: Build Android
run: ./gradlew bundleRelease
working_directory: apps/mobile/android

The working_directory field lets you run commands in subdirectories. This is useful for monorepos where the main package.json is at the root, but native build files are in subdirectories.

RunnerHub automatically caches all your dependencies:

  • Node modules — Cached via package-lock.json, yarn.lock, or pnpm-lock.yaml
  • CocoaPods dependencies — Cached via Podfile.lock (iOS)
  • Gradle dependencies — Cached via gradle wrappers (Android)
  • Xcode DerivedData — Cached per platform and branch
  • Metro bundler cache — Cached at node_modules/.cache/metro (React Native specific)

After the first build, subsequent builds with the same dependencies are nearly instant. See the Caching guide for details.

For App Store, TestFlight, Google Play, or Firebase distribution:

name: React Native Release
platform: react-native
environment:
node: lts
xcode: "16.4"
android_sdk: 34
triggers:
- push
branches:
- main
- release/*
jobs:
build-ios-release:
name: Build iOS for TestFlight
steps:
- name: Install dependencies
run: npm install
- name: Install pods
run: |
cd ios
pod install
- name: Build iOS Release
run: |
cd ios
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Release \
-derivedDataPath build \
build
artifacts:
- ios/build/**/*.app
- ios/build/**/*.dSYM
build-android-release:
name: Build Android for Google Play
steps:
- name: Install dependencies
run: npm install
- name: Build Android Release Bundle
run: |
cd android
./gradlew bundleRelease
artifacts:
- android/app/build/outputs/bundle/release/**/*.aab

Once signed via the RunnerHub Code Signing tab, you can attach Deploy configurations to automatically upload to TestFlight (iOS) or Google Play (Android).

Add secrets and configuration via the dashboard:

steps:
- name: Build
run: |
cd ios
xcodebuild -workspace App.xcworkspace -scheme App build
env:
API_URL: $API_URL
API_KEY: $API_KEY

Finding your scheme name:

Terminal window
xcodebuild -workspace ios/App.xcworkspace -list

Debugging pod issues:

- name: Debug pods
run: |
cd ios
pod repo update
pod install --repo-update

Building for a specific simulator:

- name: Build for iPhone 15
run: |
cd ios
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 15' \
build

Metro bundler:

Metro bundler cache is automatically cached at node_modules/.cache/metro and restored between builds using the platform and branch as keys. This provides significant speedup for subsequent builds on the same branch.

If you need to pre-bundle or use explicit bundling steps:

- name: Bundle
run: |
npx react-native bundle \
--platform ios \
--dev false \
--entry-file index.js \
--bundle-output ios/main.jsbundle \
--assets-dest ios
- name: Build
run: |
cd ios
xcodebuild -workspace App.xcworkspace -scheme App build

Node version: Your specified environment.node version is automatically activated before any steps run. No need to manually set up fnm, nvm, or nodenv — it’s pre-configured on the RunnerHub agents.

Dual App Setup

On the dashboard, create a React Native app to enable dual signing (both Apple and Android) in one place.

Advanced Recipes

Learn multi-job strategies, matrix builds, and platform-specific patterns in the React Native cookbook.

Pipeline Reference

Explore all YAML options, including environment.node, environment.ruby, and multi-job DAGs in the pipeline reference.