Shipping & Release· 5 min read

A CI/CD Pipeline for React Native: EAS, GitHub Actions, and Fastlane

A React Native CI/CD pipeline for 2026: checks on every pull request, preview builds, and store releases with EAS Workflows, GitHub Actions or Fastlane.

By

Full-Stack, Mobile & AI Engineer

Most React Native teams start with releases done by hand: someone runs a build on their laptop, uploads it, fills in forms and hopes nothing was forgotten. It works until that person is on holiday, or until the day a release goes out with the wrong environment variables.

A React Native CI/CD pipeline makes every release the same boring sequence: checks on every pull request, a preview build for testers, a production build and submission when you tag a release, and over-the-air updates for small fixes. Here's how to set one up with Expo's EAS Workflows, with GitHub Actions, or with Fastlane for bare projects.

Key takeaways

  • Every pull request should run type checks, linting and unit tests. That alone catches most broken builds.
  • Merging to main should produce something testers can install: a preview build or an over-the-air update to a preview channel.
  • Tagging a release should build, sign and submit to both stores automatically.
  • EAS Workflows runs builds, submissions and updates from YAML files in your repo; GitHub Actions can drive the same steps; Fastlane suits bare projects with their own build machines.
  • Keep secrets in the CI system, never in the repository: Expo token, App Store Connect API key, Google service account.

The pipeline, stage by stage

TriggerWhat runsOutput
Pull requestType check, lint, unit tests, optionally E2E on key flowsPass or fail on the PR
Merge to mainPreview build or OTA update to the preview channelTesters install or receive the change
Release tag (e.g. v1.4.0)Production builds for iOS and Android, then store submissionBuilds in TestFlight and Play Console
JavaScript-only hotfixOTA update to the production channel, as a percentage rolloutFix live without a store review
After releaseCrash and performance monitoringAlerts if crash rate rises

Option 1: EAS Workflows

EAS Workflows lives in your repository under .eas/workflows/ and runs on Expo's infrastructure, so build and submit steps don't need any other CI. Link your GitHub repository to the EAS project, then add a workflow:

# .eas/workflows/release.yml
name: Release to stores

on:
  push:
    branches: ['main']

jobs:
  build_ios:
    type: build
    params:
      platform: ios
      profile: production
  build_android:
    type: build
    params:
      platform: android
      profile: production
  submit_ios:
    needs: [build_ios]
    type: submit
    params:
      build_id: ${{ needs.build_ios.outputs.build_id }}
  submit_android:
    needs: [build_android]
    type: submit
    params:
      build_id: ${{ needs.build_android.outputs.build_id }}

You can also run a workflow on demand with eas workflow:run .eas/workflows/release.yml. A separate workflow with a job of type: update and a branch parameter can publish over-the-air updates to a preview channel on every merge.

Option 2: GitHub Actions

If your checks already run in GitHub Actions, add the EAS CLI with Expo's official action and drive builds and updates from there:

# .github/workflows/ci.yml
name: CI
on:
  pull_request:
  push:
    branches: [main]

jobs:
  checks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npx tsc --noEmit
      - run: npx eslint .
      - run: npx jest --ci

  preview-update:
    if: github.ref == 'refs/heads/main'
    needs: checks
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}
      - run: npm ci
      - run: eas update --branch preview --message "${{ github.sha }}" --non-interactive

For store releases, add a job on tag pushes that runs eas build --platform all --profile production --auto-submit --non-interactive. The builds still run on Expo's servers; GitHub Actions just starts them.

Option 3: Fastlane for bare projects

If you maintain the native projects yourself and build on your own macOS runners, Fastlane automates signing (match), building (gym for iOS, Gradle tasks for Android) and uploading (pilot to TestFlight, supply to Google Play). It's more setup and more maintenance, but gives complete control over the build environment.

From the field: My release work covers EAS Build, Submit and Update, plus Fastlane with GitHub Actions where a project needs its own pipeline, phased rollouts and crash triage in Sentry. The choice of tool matters less than the rule behind it: no release should depend on one person's laptop.

Which option to choose

SituationBest fit
Expo project, small team, wants the least setupEAS Workflows
Existing GitHub Actions checks and conventionsGitHub Actions starting EAS builds and updates
Bare project with custom native build stepsFastlane on macOS runners (optionally with EAS for some parts)
Strict rule that code never leaves company infrastructureFastlane or local EAS builds on self-hosted runners

GitLab CI and Jenkins

The pipeline doesn't change with the CI product; the runners do. Linux runners are enough for linting, tests and anything that hands builds to EAS with an EXPO_TOKEN. Building iOS yourself needs macOS: GitLab offers hosted macOS runners on its paid tiers, and Jenkins needs your own Mac agents. A common GitLab or Jenkins setup runs checks on Linux and calls eas build --non-interactive --no-wait for the native builds, which avoids maintaining Macs at all.

React Native CI/CD tools compared

ToolWhat it's best at
EAS WorkflowsExpo projects; builds, updates, submissions and Maestro tests in one place
GitHub ActionsChecks and orchestration for code already on GitHub
GitLab CIThe same, for GitLab, with hosted macOS runners on paid tiers
Bitrise, CodemagicMobile-focused hosted CI with macOS machines and signing helpers
JenkinsFull control on your own infrastructure, at the cost of maintaining it
FastlaneNot a CI service: the scripting layer for signing, builds and uploads inside any of the above

What to test, and where

Not every check belongs on every commit. A practical split:

StageTestsWhy there
Every pull requestType check, lint, unit tests, component testsFast (a few minutes), catches most breakage
Merge to mainBuild a preview app; optionally E2E tests with Maestro on key flowsSlower, but proves the app actually launches
Before a store releaseFull E2E suite on real or cloud devices, manual smoke test on a release buildHighest confidence where it matters most

Keep the pull-request stage under ten minutes. If it gets slower, developers stop waiting for it, and the pipeline stops protecting you.

Branches, tags and release notes

A simple convention keeps the pipeline predictable:

  • main is always releasable. Feature work merges through pull requests with green checks.
  • Every merge to main updates the preview channel, so testers always have the latest build or update.
  • A version tag starts a store release, for example v1.4.0. The tag names the version, and EAS remote versioning handles build numbers.
  • Release notes come from merged pull requests, written for users, not as a raw commit log.
  • Hotfixes branch from the release tag, get a patch version (v1.4.1), and ship either over the air or as a new build, depending on whether native code changed.

The point isn't the exact naming. It's that anyone on the team can answer "what's in this release, and how did it get to users?" without asking the one person who usually ships.

Secrets you'll need

  • Expo access token (EXPO_TOKEN) for any CI that runs the EAS CLI.
  • App Store Connect API key for submitting iOS builds without a password and two-factor prompt.
  • Google Play service account key (JSON) with release permissions on your app.
  • Environment variables for each profile, stored as EAS environment variables or CI secrets, not committed to the repo.

FAQ

What is the best CI/CD for React Native?

For Expo projects, EAS Workflows is the simplest complete option, because builds, store submission and over-the-air updates run in one place. GitHub Actions is a good choice when you already use it for checks. Fastlane suits bare projects that build on their own macOS machines.

Can GitHub Actions build iOS apps for React Native?

Yes, either by starting EAS builds (which run on Expo's macOS machines) or by building directly on GitHub's macOS runners with Xcode and Fastlane. Starting EAS builds is simpler; macOS runners give more control at higher cost and setup effort.

How do I automate App Store and Google Play submission?

Use EAS Submit, either as a submit job in an EAS workflow or with --auto-submit on the build command, authenticated with an App Store Connect API key and a Google Play service account. Fastlane's pilot and supply do the same for bare pipelines.

Should every merge create a new store build?

No. Store builds are best for native changes and planned releases. For JavaScript-only changes, an over-the-air update to a preview channel is faster for testers, and a percentage rollout to production is the safest way to ship small fixes.

Next steps

If your releases still depend on someone's laptop, I can set up a pipeline that checks every pull request and ships to both stores from a tag. Get in touch, or see my services.

Sources

Bhuwanesh Sisodia

Bhuwanesh Sisodia

Full-Stack, Mobile & AI Engineer

Bhuwanesh Sisodia is a Full-Stack, Mobile & AI Engineer based in Mumbai, with 7+ years of experience and 50+ apps shipped to the App Store and Google Play. He works independently with startups and product teams worldwide.

About meGitHubLinkedInX

Related