CI/CD for React Native with GitHub Actions
React Native CI is a JavaScript pipeline that happens to produce native apps.
A React Native app is a JavaScript project with native build steps bolted on. Most of your CI -- installing dependencies, linting, and running Jest -- is plain Node and runs on Linux. The Android build runs on Linux too; only the iOS build needs macOS. This recipe sets up the Linux path with caching.
What the pipeline does
- Installs Node and restores the npm cache.
- Lints the codebase and runs the Jest test suite.
- Assembles an Android release APK via Gradle.
- Leaves the iOS build to a separate macOS job.
The workflow
Use setup-node caching for npm and setup-java for the Android Gradle build in the same Linux job.
name: React Native CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test -- --ci
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
cache: gradle
- name: Build Android release
run: cd android && ./gradlew assembleReleaseNotes for this platform
The JavaScript stages (install, lint, Jest) plus the Android Gradle build all run on Linux, so they should use your cheapest, fastest runner class. Spin up a macos runner only for the iOS build (npx pod-install then xcodebuild), and gate it to PRs against your release branch to avoid burning expensive macOS minutes on every commit. Managed runners retry transient npm and CocoaPods network failures automatically, which is a common cause of red builds in React Native CI.
Key takeaways
- Most React Native CI is a Node pipeline that runs on cheap Linux runners.
- The Android build runs on Linux; only iOS needs an expensive macOS job.
- Cache npm and Gradle to cut repeated install time on every run.