0306090120150180210240270300330km/h
0 km/h
ADIT JANGID

How to Set Up a CI/CD Pipeline with GitHub Actions for Frontend Projects

A complete step-by-step guide to automating linting, testing, building, and deploying frontend applications using GitHub Actions.

How to Set Up a CI/CD Pipeline with GitHub Actions for Frontend Projects

Modern software development moves fast. In a collaborative team, manually running tests, verifying code formats, building static assets, and deploying changes to servers can lead to human error, inconsistencies, and slower release cycles.

Continuous Integration and Continuous Deployment (CI/CD) solves these bottlenecks. By automating code validation and deployment, you ensure that every pull request meets production quality standards before it reaches your users.

In this tutorial, we will write a production-ready GitHub Actions workflow designed specifically for modern frontend applications (React, Next.js, Vue). This pipeline will run quality checks on pull requests and deploy your application automatically when code merges into the main branch.


The CI/CD Pipeline Stages

Our pipeline consists of two parallel workflows:

graph TD
    A[Code Push / Pull Request] --> B{GitHub Actions Trigger}
    B -->|Pull Request| C[Job 1: Code Quality Gate]
    C --> C1[Checkout Code]
    C --> C2[Set up Node & Cache]
    C --> C3[Install Dependencies]
    C --> C4[Run Linter & Prettier]
    C --> C5[Run Unit & E2E Tests]
    
    B -->|Merge to Main| D[Job 2: Build & Deploy]
    D --> D1[Build Production Bundle]
    D --> D2[Deploy to Hosting Service]
  • Continuous Integration (CI): Triggers on pull requests. It pulls the code, installs dependencies, verifies code syntax (linting), checks formatting, and runs unit tests.
  • Continuous Deployment (CD): Triggers when changes are pushed or merged into the default branch (main or master). It compiles the project and deploys it to your hosting provider.

Step 1: Configuring Environment Credentials

To automate deployments, GitHub Actions needs permission to speak to your hosting provider (such as Vercel, Netlify, or AWS). Never hardcode API tokens or deployment keys directly inside your codebase.

  1. Go to your repository on GitHub.
  2. Navigate to Settings > Secrets and variables > Actions.
  3. Under Repository Secrets, click New repository secret.
  4. Add your secrets:
    • VERCEL_TOKEN: Your API token generated in your Vercel Account settings.
    • VERCEL_PROJECT_ID: The project ID of your application.
    • VERCEL_ORG_ID: Your Vercel team/individual organization ID.

Step 2: Designing the Workflow Configuration

GitHub Actions workflows are declared in YAML files inside the .github/workflows/ directory.

Create a new file in your project path: .github/workflows/ci-cd.yml. Copy the configuration below:

name: Frontend CI/CD Pipeline

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

# Cancel in-progress builds for the same branch if a new commit is pushed
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  # Stage 1: Continuous Integration (Lint, Format, Test)
  quality-gate:
    name: Code Quality Check
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm' # Automatically caches node_modules in GitHub runner

      - name: Install Dependencies
        run: npm ci # Performs a clean install matching package-lock.json

      - name: Run Linter
        run: npm run lint

      - name: Verify Code Formatting
        run: npx prettier --check .

      - name: Run Unit Tests
        run: npm test -- --watchAll=false --passWithNoTests

  # Stage 2: Continuous Deployment (Triggered only on main branch merges after checks pass)
  deploy:
    name: Deploy to Hosting
    needs: quality-gate # Require quality-gate job to pass before starting
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install Dependencies
        run: npm ci

      - name: Install Vercel CLI
        run: npm install --global vercel@latest

      - name: Pull Vercel Environment Information
        run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
        env:
          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

      - name: Build Project Artifacts
        run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
        env:
          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

      - name: Deploy to Vercel Production
        run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}
        env:
          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

Step 3: Deconstructing the Workflow Configuration

Let's dissect the critical configurations inside this pipeline:

  1. on Trigger: We configure this workflow to run on pushes and pull requests pointing to the main branch.
  2. concurrency: Avoid running multiple workflows on the same pull request if someone pushes multiple commits in rapid succession. cancel-in-progress: true terminates previous runs, saving GitHub Actions billing minutes.
  3. npm ci: Standard npm install can update package-lock.json dynamically if peer dependency conflicts occur. npm ci strictly installs dependencies configured in your lockfile, failing if lock discrepancies exist.
  4. Dependency Caching (cache: 'npm'): Re-downloading dependencies on every single run can double build times. Caching stores the global npm cache on GitHub’s servers and loads it back during setup.
  5. needs: quality-gate: We establish sequential dependency between jobs. The deployment step will only execute once all tests and formatting gates finish without throwing errors.
  6. if Condition: Prevents the deploy stage from triggering during standard Pull Request validation. It will only execute when a direct push or a merged PR updates the production main branch.

Advanced Options: Adding Cypress / Playwright E2E Testing

If your app requires end-to-end browser tests, you should add them to your CI flow. However, E2E tests require a built application and a local web server to execute commands. Here is how you can expand the quality-gate workflow to run E2E tests:

      # Inside the quality-gate steps:
      - name: Run E2E Tests (Playwright)
        run: |
          npx playwright install --with-deps
          npm run build
          npx playwright test

Conclusion

You have successfully automated your software testing and deployment workflows using GitHub Actions! By caching dependencies, checking styling and structural issues during pull requests, and triggering server deployments only on validated main merges, you have created a robust, developer-friendly delivery process.

As your engineering team scales, you can introduce custom integrations like Matrix builds to test on multiple Node.js environments simultaneously, or write steps sending slack/discord hooks alerting developers of failing runs.

CI-CDGitHub ActionsDevOpsFrontendNext.js

Related Posts

Interested in working together?

Let's translate complex software and automation problems into clean, high-performance systems.