A dependable React Native CI/CD pipeline should make every change easier to verify and every release easier to reproduce. This guide shows how to combine GitHub Actions with Expo Application Services (EAS) for pull-request checks, preview builds, production builds, and Android or iOS submission. It also provides a repeatable way to estimate pipeline effort, identify assumptions, manage secrets, and decide when the workflow needs to be recalculated.
Overview
A practical React Native CI/CD workflow separates fast feedback from release operations. Pull requests should run checks that are quick enough to use on every change. Preview builds should help reviewers test a realistic app package without granting production credentials. Production builds and store submissions should require an intentional trigger, protected environment, or approval.
A useful stage matrix looks like this:
| Stage | Trigger | Typical work | Expected result |
|---|---|---|---|
| Quality checks | Pull request | Install dependencies, lint, TypeScript checks, unit tests | Pass or fail before review |
| Preview build | Selected branch or manual run | Create a non-production Android or iOS build | Testable artifact for reviewers |
| Production build | Release branch, tag, or approval | Build signed app bundles with the intended environment | Release artifact with an identifiable version |
| Submission | Manual approval or release workflow | Upload the approved artifact to the relevant store process | Submission record and audit trail |
EAS is a natural fit for Expo-based projects and can also support projects with native folders, provided the project and credentials are configured for the selected build profiles. Teams using a fully native React Native setup can use the same GitHub Actions principles while choosing local Gradle and Xcode commands or another build service. Review the related Expo versus React Native CLI guide before selecting the build boundary.
How to estimate the workflow
Estimate a pipeline by measuring work per change, not by counting YAML lines. Record the following values for a representative week:
- P: pull requests that require CI.
- T: average minutes for one quality-check run.
- R: preview or release builds per week.
- B: average minutes for one build, including queue time if that affects delivery.
- S: manual submission and verification minutes per release.
- F: avoidable rerun or failure minutes, such as broken caches, missing secrets, or incorrect profiles.
A simple weekly workload estimate is:
CI minutes = (P × T) + (R × B) + S + F
This is not a billing forecast. It is an operational baseline. If you want to compare designs, calculate the same inputs for each option. For example, compare a workflow that builds every pull request with one that runs checks on every pull request but creates preview builds only for a dedicated branch. The second design may reduce build minutes and signing exposure while preserving review coverage.
Also track outcomes: median time from merge to testable build, failed-run rate, time to diagnose a failed run, and the number of releases requiring manual repair. A slightly slower pipeline can be preferable if it produces reproducible artifacts and clearer failure messages.
Inputs and assumptions
Repository and environment inputs
Define the Node.js version, package manager, lockfile, Expo SDK or React Native version, native build tools, and EAS CLI version used by the workflow. Pinning or explicitly selecting these inputs makes failures easier to reproduce. Avoid relying on an unexamined runner default.
Keep environment configuration separate from credentials. Public runtime configuration can be supplied through an EAS environment or build profile, while signing keys, store tokens, and service-account files belong in protected GitHub or EAS secrets. The React Native environment variables and secrets guide provides a useful review checklist.
Branch and profile assumptions
Use a small, explicit profile set such as development, preview, and production. Map each profile to an environment, distribution method, application identifier, and credential set. Do not let a preview branch silently select production credentials.
Protect the production environment in GitHub. Require review for workflows that can submit an app, and keep submission separate from compilation when possible. A build can then be inspected, tested, and approved before it is uploaded.
Example quality-check workflow
The following is a starting pattern. Adjust action versions, package-manager commands, and Node versions to match the repository; verify current syntax before adopting it.
name: checks
on:
pull_request:
push:
branches: [main]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm run lint --if-present
- run: npx tsc --noEmit
- run: npm test -- --runInBand
Example EAS build and submit workflow
Store the EAS token as a protected repository or environment secret. Use a preview profile for review builds and a production profile only from a controlled release workflow.
name: mobile-release
on:
workflow_dispatch:
inputs:
profile:
required: true
default: preview
type: choice
options: [preview, production]
jobs:
build:
runs-on: ubuntu-latest
environment: mobile-release
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npx eas-cli build --platform all --profile ${{ inputs.profile }} --non-interactive
env:
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
Submission can be a separate job or workflow, for example with the appropriate eas submit command and platform selection. Keep store credentials in the credential system intended for the project, confirm the artifact and version, and follow the current store-specific submission requirements. Never print tokens, provisioning data, keystores, or service-account contents in logs.
Worked examples
Suppose a team has 18 pull requests per week, with quality checks averaging 7 minutes. It creates 4 preview or release builds averaging 22 minutes, spends 35 minutes on a release submission, and loses 25 minutes to avoidable reruns.
(18 × 7) + (4 × 22) + 35 + 25 = 293 minutes per week
The useful conclusion is not the absolute number. It is where the work goes: pull-request checks account for most of the routine run time, while build and submission work deserves stronger controls. The team might retain checks on every pull request, move preview builds to a review branch, cache dependencies, and investigate the reruns before adding more parallel jobs.
For a second comparison, assume production builds are currently created on every merge. If only tagged releases need production artifacts, changing the trigger can reduce unnecessary signing operations and make the release history easier to audit. Before making that change, confirm that reviewers still have an appropriate preview path and that emergency fixes have a documented manual trigger.
Failure triage table
| Symptom | Likely area | First check |
|---|---|---|
| Dependency installation differs from local | Node or lockfile mismatch | Compare the lockfile, package manager, and configured Node version. |
| TypeScript or tests fail only in CI | Environment or test isolation | Inspect missing variables, timezone assumptions, filesystem paths, and test ordering. |
| EAS build stops before compilation | Profile or credential configuration | Run the selected profile locally or with a safe diagnostic command and verify its environment. |
| Submission is rejected or cannot authenticate | Store metadata or credentials | Check the target application, version, credential scope, and current submission instructions. |
| Build succeeds but reviewers cannot install it | Distribution settings | Confirm the preview profile, signing method, device access, and artifact format. |
When to recalculate
Revisit the estimate and workflow whenever an input changes: a new React Native or Expo version, a changed build image, a new package manager, a branch-model change, a higher release cadence, renewed certificates, altered store credentials, or a change in EAS or GitHub Actions behavior. Pricing and usage limits may also change, so consult the current provider documentation before treating minutes or service usage as a budget.
Recalculate after three to five representative release cycles rather than after one unusually large failure. Compare planned and observed values for check time, build time, queue time, reruns, and manual intervention. Update the matrix if a stage no longer provides useful feedback or if production operations have become too easy to trigger accidentally.
For the next iteration, create a release branch or tag, run quality checks, generate a preview artifact, and document who approves production submission. Then test the rollback path: identify the last known-good version, preserve its artifact and release metadata, pause automatic submission, and decide whether the recovery requires a new store submission or a server-side configuration change. Pair this pipeline review with the React Native production readiness checklist and the release error and crash-reporting workflow before the next release train.