Skip to main content

Continuous Integration for React Native Apps with Fastlane and GitHub Actions

· 10 min read
Mobile Developer
Last updated on August 14, 2025

continus integration on react native apps

Continuous Integration for React Native Apps with Fastlane and GitHub Actions

Building a React Native app is fun, but manually testing, building, and deploying every change can slow you down. Continuous integration (CI) automates these tasks, letting you focus on creating awesome features. In this beginner-friendly guide, we’ll set up a CI pipeline for your React Native app using Fastlane, GitHub Actions, and Expo Application Services (EAS) with Expo SDK 51 and React Native 0.75.4. We’ll automate builds for iOS (Xcode 16+, iOS 18 SDK) and Android (SDK 35), run tests, and deploy OTA updates and store releases. No CI experience is needed, and we’ll explain key terms along the way. For prebuilt CI setups, check out templates at Instamobile or Dopebase.

By the end, you’ll have a fully automated pipeline for your app. Let’s dive in!


Prerequisites

Before we start, ensure you have these tools and accounts:

  • Node.js (v20.x or later, LTS recommended): Download from nodejs.org.
  • npm (v10.x or later, included with Node.js).
  • EAS CLI: For Expo project management.
  • Android Studio (2024.3.2 or later): With Android SDK 35 and NDK r25+.
  • Xcode (16+): For iOS builds, with iOS 18 SDK (macOS only).
  • A code editor like VS Code.
  • Git: For version control.
  • GitHub Account: For hosting your repository and GitHub Actions.
  • Apple Developer Account: For iOS builds ($99/year, developer.apple.com).
  • Google Play Developer Account: For Android builds ($25 one-time, play.google.com/console).
  • Ruby (3.2 or later): For Fastlane, installed via Homebrew (macOS) or system package manager (Linux).
  • macOS: Required for iOS builds.

What is CI/CD? Continuous Integration (CI) automatically builds and tests your code when you push changes, catching bugs early. Continuous Deployment (CD) automates releasing those changes to users, like app stores or OTA updates. It’s like having a tireless assistant for your workflow! Learn more in GitHub’s CI/CD guide.

Ready? Let’s set up your project!


Step 1: Create and Configure Your React Native Project

Let’s create a React Native project optimized for CI with EAS.

  1. Install EAS CLI globally:

    npm install -g eas-cli
  2. Create a new project called MyApp:

    npx create-expo-app MyApp --template blank-typescript

    This uses Expo SDK 51, supporting React Native 0.75.4, with TypeScript for type safety.

  3. Navigate to your project folder:

    cd MyApp
  4. Configure app.config.js for flexibility:

    export default () => ({
    name: "MyApp",
    slug: "myapp",
    version: "1.0.0",
    orientation: "portrait",
    icon: ".https://docs.instamobile.io/assets/icon.png",
    splash: {
    image: ".https://docs.instamobile.io/assets/splash.png",
    resizeMode: "contain",
    backgroundColor: "#ffffff"
    },
    ios: {
    bundleIdentifier: "com.myapp.app",
    buildNumber: "1.0.0"
    },
    android: {
    package: "com.myapp.app",
    versionCode: 1
    },
    updates: {
    enabled: true,
    url: "https://u.expo.dev/your-project-id",
    checkAutomatically: "ON_LOAD",
    fallbackToCacheTimeout: 0
    }
    });
    • Use unique bundleIdentifier (iOS) and package (Android) in reverse-domain format (e.g., com.yourcompany.myapp).
    • Prepare a 1024x1024 PNG icon (iOS) and 512x512 PNG icon (Android) in assets/.
  5. Test locally:

    npx expo start

    Press i for iOS or a for Android to run on a simulator/emulator.

Summary: You’ve set up a React Native project with Expo SDK 51 and a dynamic app.config.js, ready for CI automation.


Mega Bundle Sale is ON! Get ALL of our React Native codebases at 90% OFF discount 🔥

Get the Mega Bundle

Step 2: Set Up Fastlane

Fastlane automates tasks like building and deploying apps. Let’s configure it for iOS and Android.

What is Fastlane? Fastlane is a tool that automates repetitive tasks, like generating app builds or uploading to app stores. It saves time by scripting complex processes.

  1. Install Fastlane:

    • macOS (for iOS builds):
      brew install fastlane
    • Linux (for Android CI on Ubuntu):
      sudo gem install fastlane -NV
  2. Initialize Fastlane for iOS:

    cd ios && fastlane init

    Select manual setup and create ios/Fastfile:

    default_platform(:ios)

    platform :ios do
    desc "Build and sign iOS app"
    lane :build_ios do
    setup_ci
    app_store_connect_api_key(
    key_id: ENV["APP_STORE_CONNECT_KEY_ID"],
    issuer_id: ENV["APP_STORE_CONNECT_ISSUER_ID"],
    key_content: ENV["APP_STORE_CONNECT_KEY_CONTENT"]
    )
    match(type: "appstore", readonly: true)
    gym(
    scheme: "MyApp",
    export_method: "app-store",
    output_directory: "./build",
    output_name: "MyApp.ipa"
    )
    end
    end
  3. Initialize Fastlane for Android:

    cd android && fastlane init

    Update android/Fastfile:

    default_platform(:android)

    platform :android do
    desc "Build and sign Android AAB"
    lane :build_android do
    gradle(
    task: "bundle",
    build_type: "Release",
    project_dir: "android"
    )
    end
    end
  4. Set up Fastlane Match for iOS credentials:

    fastlane match init

    Create a private Git repository for certificates and provisioning profiles, then run:

    fastlane match appstore

Summary: You’ve configured Fastlane for iOS and Android, with secure credential management for iOS builds.


Step 3: Configure GitHub Actions for CI

GitHub Actions automates your CI pipeline. Let’s create a workflow to build and test your app.

What are GitHub Actions? GitHub Actions is a platform that runs automated workflows (like building or testing) when you push code to GitHub. It’s like a robot that handles repetitive tasks for you. See GitHub’s guide.

  1. Create a GitHub repository and push your project:

    git init
    git add .
    git commit -m "Initial commit"
    git remote add origin <your-repo-url>
    git push -u origin main
  2. Create .github/workflows/ci.yml:

    name: React Native CI
    on:
    push:
    branches: [main]
    pull_request:
    branches: [main]
    jobs:
    test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
    with:
    node-version: 20
    - name: Install dependencies
    run: npm install
    - name: Run tests
    run: npm test
    build-ios:
    runs-on: macos-14
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
    with:
    node-version: 20
    - uses: ruby/setup-ruby@v1
    with:
    ruby-version: 3.2
    - name: Install Fastlane
    run: gem install fastlane
    - name: Set up Fastlane Match
    env:
    MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
    MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
    run: |
    echo "${{ secrets.MATCH_GIT_PRIVATE_KEY }}" > match_key
    chmod 600 match_key
    export MATCH_GIT_PRIVATE_KEY_PATH=$(pwd)/match_key
    fastlane match appstore --readonly
    - name: Build iOS
    env:
    APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }}
    APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
    APP_STORE_CONNECT_KEY_CONTENT: ${{ secrets.APP_STORE_CONNECT_KEY_CONTENT }}
    run: cd ios && fastlane build_ios
    - name: Upload iOS artifact
    uses: actions/upload-artifact@v4
    with:
    name: ios-build
    path: ios/build/MyApp.ipa
    build-android:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
    with:
    node-version: 20
    - uses: actions/setup-java@v4
    with:
    java-version: 17
    - name: Install Fastlane
    run: sudo gem install fastlane -NV
    - name: Install dependencies
    run: npm install
    - name: Set up Keystore
    env:
    KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
    KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
    KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
    KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
    run: |
    echo $KEYSTORE_BASE64 | base64 -d > android/app/my-upload-key.keystore
    echo "MYAPP_UPLOAD_STORE_FILE=my-upload-key.keystore" >> android/gradle.properties
    echo "MYAPP_UPLOAD_KEY_ALIAS=$KEY_ALIAS" >> android/gradle.properties
    echo "MYAPP_UPLOAD_STORE_PASSWORD=$KEYSTORE_PASSWORD" >> android/gradle.properties
    echo "MYAPP_UPLOAD_KEY_PASSWORD=$KEY_PASSWORD" >> android/gradle.properties
    - name: Build Android
    run: cd android && fastlane build_android
    - name: Upload Android artifact
    uses: actions/upload-artifact@v4
    with:
    name: android-build
    path: android/app/build/outputs/bundle/release/app-release.aab
  3. Add GitHub secrets (Settings > Secrets and variables > Actions):

    • MATCH_PASSWORD: Fastlane Match password.
    • MATCH_GIT_URL: Match repository Git URL.
    • MATCH_GIT_PRIVATE_KEY: SSH private key (base64-encoded).
    • APP_STORE_CONNECT_KEY_ID, APP_STORE_CONNECT_ISSUER_ID, APP_STORE_CONNECT_KEY_CONTENT: From App Store Connect API key (generate at appstoreconnect.apple.com).
    • KEYSTORE_BASE64: Base64-encoded Android keystore (cat my-upload-key.keystore | base64).
    • KEYSTORE_PASSWORD, KEY_ALIAS, KEY_PASSWORD: From keystore creation.

Summary: You’ve created a GitHub Actions workflow to automate testing and building for iOS and Android, using macos-14 for iOS compatibility.


Step 4: Add Testing to the CI Pipeline

Let’s add automated tests to ensure your code is solid.

  1. Install testing dependencies:

    npm install --save-dev jest @types/jest ts-jest @testing-library/react-native @testing-library/jest-native
  2. Configure Jest in package.json:

    {
    "scripts": {
    "test": "jest"
    },
    "jest": {
    "preset": "react-native",
    "transform": {
    "^.+\\.tsx?$": "ts-jest"
    },
    "setupFilesAfterEnv": ["@testing-library/jest-native/extend-expect"]
    }
    }
  3. Create a test in src/__tests__/App.test.tsx:

    import { render } from '@testing-library/react-native';
    import App from '../App';

    test('renders welcome text', () => {
    const { getByText } = render(<App />);
    expect(getByText('Welcome to MyApp!')).toBeTruthy();
    });
  4. Update .github/workflows/ci.yml to run tests (already included in Step 3).

  5. Test locally:

    npm test

Summary: You’ve added Jest and React Native Testing Library to your CI pipeline, catching bugs early.


Step 5: Automate OTA Updates with EAS Update

Let’s automate over-the-air (OTA) updates for instant deployments using EAS Update.

  1. Install expo-updates:

    npm install expo-updates

    Check the latest compatible version for Expo SDK 51 in Expo’s package documentation.

  2. Configure app.config.js (done in Step 1).

  3. Add update logic to src/App.tsx:

    import { useEffect } from 'react';
    import { View, Text } from 'react-native';
    import * as Updates from 'expo-updates';

    export default function App() {
    useEffect(() => {
    const checkForUpdates = async () => {
    try {
    const update = await Updates.checkForUpdateAsync();
    if (update.isAvailable) {
    await Updates.fetchUpdateAsync();
    await Updates.reloadAsync();
    }
    } catch (e) {
    console.error('Update check failed:', e);
    }
    };
    if (!__DEV__) {
    checkForUpdates();
    }
    }, []);

    return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
    <Text>Welcome to MyApp!</Text>
    </View>
    );
    }
  4. Configure EAS Update:

    eas update:configure

    Update app.config.js with the generated project-id.

  5. Update .github/workflows/ci.yml to publish OTA updates:

    name: React Native CI
    on:
    push:
    branches: [main]
    pull_request:
    branches: [main]
    jobs:
    test:
    # ... (same as Step 3)
    build-ios:
    # ... (same as Step 3)
    build-android:
    # ... (same as Step 3)
    publish-ota:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
    with:
    node-version: 20
    - name: Install EAS CLI
    run: npm install -g eas-cli
    - name: Install dependencies
    run: npm install
    - name: Publish OTA update
    env:
    EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
    run: eas update --branch production
  6. Add EXPO_TOKEN to GitHub secrets:

    • Run eas login and eas whoami locally to get your token.
    • Add it in Settings > Secrets and variables > Actions.

Summary: You’ve automated OTA updates with EAS Update, enabling instant JavaScript and asset deployments.


Step 6: Deploy to App Stores

Let’s automate deployments to the App Store and Play Store using EAS and Fastlane.

  1. Update ios/Fastfile for App Store Connect API:

    platform :ios do
    desc "Build and deploy iOS app"
    lane :deploy_ios do
    setup_ci
    app_store_connect_api_key(
    key_id: ENV["APP_STORE_CONNECT_KEY_ID"],
    issuer_id: ENV["APP_STORE_CONNECT_ISSUER_ID"],
    key_content: ENV["APP_STORE_CONNECT_KEY_CONTENT"]
    )
    match(type: "appstore", readonly: true)
    gym(
    scheme: "MyApp",
    export_method: "app-store",
    output_directory: "./build",
    output_name: "MyApp.ipa"
    )
    deliver(
    submit_for_review: false,
    force: true,
    app_identifier: "com.myapp.app"
    )
    end
    end
  2. Update android/Fastfile:

    platform :android do
    desc "Build and deploy Android AAB"
    lane :deploy_android do
    gradle(
    task: "bundle",
    build_type: "Release",
    project_dir: "android"
    )
    supply(
    package_name: "com.myapp.app",
    aab: "android/app/build/outputs/bundle/release/app-release.aab",
    track: "production",
    json_key_data: ENV["GOOGLE_PLAY_JSON_KEY"]
    )
    end
    end
  3. Update .github/workflows/ci.yml to use deploy_ios and deploy_android lanes (replace build_ios and build_android in Step 3).

  4. Add additional GitHub secrets:

Summary: Your CI pipeline now automates App Store and Play Store deployments with secure API key authentication.


Troubleshooting Tips

  • Fastlane Match Fails: Verify MATCH_GIT_URL and MATCH_GIT_PRIVATE_KEY. Ensure the Match repository is accessible.
  • GitHub Actions Errors: Use macos-14 for iOS, ubuntu-latest for Android. Check secrets for typos.
  • Build Fails: Confirm Xcode 16+ (iOS) and Android SDK 35. Clear caches (rm -rf ~/Library/Developer/Xcode/DerivedData for iOS, ./gradlew clean for Android).
  • OTA Issues: Verify EXPO_TOKEN and app.config.js’s updates.url. Run eas update:configure to reset.

Looking for a custom mobile application?

Our team of expert mobile developers can help you build a custom mobile app that meets your specific needs.

Get in Touch

Conclusion

You’ve built a robust CI pipeline for your React Native app using Fastlane, GitHub Actions, and EAS with Expo SDK 51! Your app now automatically builds, tests, and deploys to the App Store, Play Store, and via OTA updates. For faster setups, explore CI-ready templates at Instamobile or Dopebase. Want more? Dive into Expo’s documentation, or join the React Native community at reactnative.dev.

Additional Resources