DEV Community

Meet Dave
Meet Dave

Posted on • Originally published at


CI/CD tooling for frontend projects using GitHub actions

Setting up continuous integration pipelines for your frontend project can help you avoid bugs & filter out problems sneaking into production. 


Photo by Nathan Dumlao on Unsplash
Filtering out the best for your end users 👆

Having CI/CD in your frontend project from the beginning can help in avoiding “broken windows“ in your project (ref: broken window theory). 

Additionally, setting up the workflow can help significantly improve developer experience (DX) & user experience (UX)

A pipeline that is setup early on in the project can help the team work with a unified purpose & set guidelines. 


This article demonstrates a continuous integration pipeline setup for a starter Next.js app deployed on Vercel living on a Github repository. The pipeline makes use open-source Github actions from the Github marketplace

The CI jobs runs synchronously. If a job fails, the process would exit.

For this article I have chosen to setup the following jobs:

  1. Linting (eslint + stylelint) - Helps with consistent DX
  2. Vercel deployment - Helps with deployment previews useful for CI
  3. Lighthouse budget checks - Helps in UX integrity & maintain consistency throughout the software lifecycle

Additional jobs you can add in your CI pipeline: 

If you find yourself using something else than the options I have chosen above. Here are some reasons why I have opinionated myself with the selected tools for this example:

  1. Why Next.js instead of Angular, Nuxt.js, etc?
    Next.js gives flexibility to statically generate a page and / or use server side rendering. Smaller overall bundle size helps in achieving better UX.

  2. Why Vercel instead of AWS Amplify or Netlify, etc?
    Deployment previews! Each PR generates a deployment preview URL. Makes it easy and efficient to run E2E tests or Lighthouse checks against a preview URL. Also since I’m using Next.js.

  3. Why Github actions instead of Gitlab or Bitbucket, etc
    Ability to use and/or write open-source Github actions.

1. Getting started

In this example I’m setting up the CI pipeline for a boilerplate Next.js app.

npx create-next-app

After the boilerplate next app is generated, I initialise the ESLint setup: 

eslint —init

ESLint initialisation will take you through a set of questions and help you set up the correct lint rules for your project. It automatically generates the configuration file & installs the dependencies.

For linting styles, I mainly prefer using stylelint, helps in enforcing css / scss conventions & avoid errors for stylesheets. Equivalent to using ESLint for Javascript files. Read more about configuring stylelint for your frontend project.

If you are writing styles in CSS-in-JS land, stylelint has a styled-components extension that you can configure as well :)

Finally, add the lint check scripts inside the package.json file so we can set it up with our CI pipeline next.

2. Creating the Github actions workflow

To write the Github actions, we need to create main.yml workflow file inside .github/workflows/

In this CI example we have 3 jobs in our workflow. 

  • Lint source code (ESLint + Stylelint)
  • If no lint errors then -> deploy on Vercel
  • If deploy build is successful then -> run performance check with Lighthouse

We get started by defining that the jobs should run whenever there is a PUSH event on any branch. 

name: CI
on: [push]

Based on your use-case, you can also chose to have different events. Checkout the different events that trigger a workflow

3. Setup the linting job

    runs-on: ubuntu-latest
      - name: Begin linting
        uses: actions/checkout@v2
          fetch-depth: 0

      - name: Use Node 12
        uses: actions/setup-node@v1
          node-version: 12.x

      - name: Use cached node_modules
        uses: actions/cache@v2
          path: node_modules
          key: nodeModules-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |

      - name: Install dependencies
        run: yarn install --frozen-lockfile

      - name: Run ESLint
        run: yarn lint

      - name: Run stylelint
        run: yarn stylelint
Enter fullscreen mode Exit fullscreen mode

This job checks if the code / changes adhere to the lint rules. As you can see, the job checks for eslint and stylelint problems.

To improve the DX and catch these lint issues while developing, it’s best to setup your IDE with the respective plugins. Since I use VSCode, here are the eslint or stylelint plugins that I use. I also like to set up the format on save in the VSCode settings.json file to automatically format & fix all fixable problems when I hit save.

4. Generate deployment previews with Vercel

    runs-on: ubuntu-latest
    needs: lint
      - uses: actions/checkout@v2
      - uses: amondnet/vercel-action@v20
        id: vercel-deployment
          github-token: ${{ secrets.GH_TOKEN }}
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.ORG_ID}}
          vercel-project-id: ${{ secrets.PROJECT_ID}}
          vercel-project-name: 'nextjs-tooling'
      preview-url: ${{ steps.vercel-deployment.outputs.preview-url }}
Enter fullscreen mode Exit fullscreen mode

This job makes the use of the opensource GitHub action: Vercel actions
The opensource action makes it easy for us to generate Vercel deployments, whether they are preview deployments or production deployments. 

Here, we are also checking if the production build succeeds or not since we are making a deployment.

Using this action in the CI pipeline is better than setting up the Vercel for Github Integration, since the Github integration is asynchronous and runs independently of the pipeline state. 

Once the deployment is complete, Vercel generates a preview-url

You may chose to run Cypress tests against the preview-url. In this article, we will be setting up the Lighthouse job which will make use of this preview-url. 

5. Run Lighthouse budget checks against the deployment previews

    runs-on: ubuntu-latest
    needs: deploy
      - uses: actions/checkout@v2
      - name: Run Lighthouse on urls and validate with lighthouserc
        uses: treosh/lighthouse-ci-action@v7
          urls: |
            ${{ needs.deploy.outputs.preview-url }}
            ${{ needs.deploy.outputs.preview-url }}/some-other-path
          budgetPath: ./budget.json
          runs: 3
Enter fullscreen mode Exit fullscreen mode

This job makes use of the opensource treosh/lighthouse-ci-action Github action. The action makes use of the Lighthouse CI and audits our deployments. The action allows us to set numerous options like 

  • testing against multiple paths
  • providing a budget path
  • number of runs (how many times the CI should audit an URL?)
  • and many more ..

In this job, setting up a budget is the most important task. As you may have known, with the Google page experience update coming up in May / June 2021, Lighthouse scores and page speed performance are going to be prioritised in search rankings. 

Therefore, setting up this Github action would help immensely for websites that rely on Google search traffic. If not taken care of in the early stage, it’s quite normal for the bundle sizes to bloat up as a website is iterated, then that results in a lower lighthouse score. This Lighthouse Github action helps us keep an eye on any discrepancy in every commit.

Based on your project, it can also be important to setup a correct Lighthouse budget. The budget for the example repo is:

6. CI in Action

Working Github repo:

Now we have all 3 jobs setup in the CI pipeline, on each push to a branch the following 3 jobs would run like so:

Screenshot 2021-04-21 at 20.58.32

Additionally, if there is a PR, vercel-actions will comment on the PR once the preview URL is deployed successfully. 

Screenshot 2021-04-16 at 15.51.02

In conclusion

Simply, any commit which violates your CI rules will not make it to production. And in the 3 jobs we have defined in this article has a core focus on UX and DX.

These pipelines setup are best suited for any frontend project since the successful pipelines give you that the extra finesse and assurance to deliver a solid, consistent end user experience & also an improved developer experience for your team & co-contributors.

Interested in building lightning fast web applications on React? We’re in search for senior frontend developers, Apply here!

Top comments (3)

ekafyi profile image

I'm just learning to use Github Actions and my first objective is indeed to run a Lighthouse test and implement a perf budget, and voila I came across this article. :)

On a side note it's interesting to see linting and formatting as part of a GH workflow. Any specific reason why you prefer to run them there instead of, say, using Husky pre-commit hook?

meetdave3 profile image
Meet Dave

Because it's possible to by-pass a pre-commit hook. Using the linting and formatting in the CI ensures the code integrity. Specially helps when working in teams

ekafyi profile image

Good point, cheers!

An Animated Guide to Node.js Event Loop

>> Check out this classic DEV post <<