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.

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 (
mainormaster). 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.
- Go to your repository on GitHub.
- Navigate to Settings > Secrets and variables > Actions.
- Under Repository Secrets, click New repository secret.
- 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:
onTrigger: We configure this workflow to run on pushes and pull requests pointing to themainbranch.concurrency: Avoid running multiple workflows on the same pull request if someone pushes multiple commits in rapid succession.cancel-in-progress: trueterminates previous runs, saving GitHub Actions billing minutes.npm ci: Standardnpm installcan updatepackage-lock.jsondynamically if peer dependency conflicts occur.npm cistrictly installs dependencies configured in your lockfile, failing if lock discrepancies exist.- 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. 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.ifCondition: 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 productionmainbranch.
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.