Level Up Coding

Coding tutorials and news. The developer homepage gitconnected.com && skilled.dev && levelup.dev

Follow publication

Deploying a Vite React TypeScript app to Github Pages using GitHub Actions, Jest, and pnpm— As a pro

Introduction

I have seen many configurations explaining how to use GitHub Actions to deploy your Vite React website. GitHub and Vite have their own, which is cool.

Nonetheless, if you want to customize what is shown in their documentation, sometimes it is not as straightforward. Let’s say we don’t want to use npm rather pnpm or yarn. Also, we would probably want to cache dependencies and test and abort deployment if the tests fail—because why not avoid disaster?

Setting up the project on GitHub

Create an empty repository, and within the repo settings, make sure the following options are checked:

  • Settings > Pages: Build and deployment to Github Pages is done from “GitHub Actions”
Settings for “Pages”
  • Actions > General: “Allow all actions and reusable workflows”
Settings for Actions / General
  • and that your workflows have read and write permissions (in the same Actions/general tab)
Settings for Workflow Permissions

Setting up the project locally

After creating the repository, create the project locally and link it to the remote. I will use pnpm as my package manager and run the react-ts template from Vite to bootstrap the project.

sudo npm i -g pnpm //install pnpm
pnpm --version // check version, if retrieved installed properly
pnpm create vite my-demo-app --template react-ts
cd my-demo-app
pnpm install
pnpm pkg set 'packageManager'='pnpm@'$(pnpm --version) // set pnpm current version as the package manager
git init
git remote add origin .... // use your link from GitHub
git add .
git commit -m "initial commit"
git push -u origin main
pnpm run dev

At this point, you should already have a running application locally:

Adding Jest and setting up tests

Many articles cover setting up Jest with Vite to test React applications using TypeScript. I won’t delve into the details here, but in short, you need to:

  • Install the following dependencies — Jest as the test runner, React Testing Library as the test framework, some transformers for file extensions like CSS or SVG, and a Jest and Node transformer —
pnpm add -D \
@testing-library/jest-dom \
@testing-library/react \
@types/jest \
identity-obj-proxy \
jest \
jest-environment-jsdom \
jest-transformer-svg \
ts-jest \
ts-node
  • Add a test script:
pnpm pkg set 'scripts.test'='jest'
  • Tweak all the configurations needed to make it work by checking this commit (talk is cheap; show me the code)
  • Run:
pnpm run test
  • And enjoy watching your tests pass (for now, dun dun dun)

Hands-on the GitHub Worfklow

First, add your repo's name to the base property within your vite.config file. For example, if my repo is https://github.com/jonatankruszewski/gh-actions-vite-ts-jest, then in base I would need to add '/gh-actions-vite-ts-jest/'.

//vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
base: '/gh-actions-vite-ts-jest/'
})

Then, let’s create the deploy workflow that will run with GitHub Actions. Let’s not forget that it should be stored inside the .github/workflows folder. We will call this file deploy.yml.

mkdir -p .github/workflows && cd $_ && touch deploy.yml

In our deploy.yml file, we will place the following:

name: Deploy to GitHub Pages
on:
push:
branches:
- main
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.1.7
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.5.0
- name: install Nodejs
uses: actions/setup-node@v3
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Run tests
run: pnpm test

build:
runs-on: ubuntu-latest
needs: test
steps:
- name: Checkout
uses: actions/checkout@v4.1.7
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.5.0
- name: install Nodejs
uses: actions/setup-node@v3
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Build project
run: pnpm run build
- name: Upload production-ready build files
uses: actions/upload-pages-artifact@v3
with:
name: github-pages
path: ./dist

deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'

permissions:
pages: write
contents: write
id-token: write
actions: read

environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: github-pages
path: ./dist
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

Understanding the workflow file

The workflow file contains three jobs: test, build, and deploy. Each job runs on a different machine and needs to perform the same steps, such as checking out the code, installing dependencies, and so on. Three jobs, three computing instances.

These jobs would run in parallel by default, but we want each to run only after the previous one finishes. That is the reason we have the depends property.

Each job contains a series of steps. Since they are run on different machines, we must download the code, install the dependencies, and so on every time. That is why the checkout action, for example, is used three times. We could have used a composite action to remove the duplicated declarations, but let’s keep it this way for now.

Since the machines are different, once the build finishes its part, we need to publish the artifact to consume it later. This is done with the uploaddownload artifact action. Actually, here we are using a more apt option for publishing to GitHub Pages, called upload-pages-artifact.

So, if you’ve reached this point where you set up the repo, testing and yml file, after pushing the changes, you should see something like this:

If the tests pass, the workflow should build and deploy to GitHub Pages

Failing a test skips the build and deploy jobs as intended:

The build and deploy steps weren’t triggered since they depend on each other, and the test step failed.

Wrapping Up

We deployed a Vite React TypeScript app to GitHub Pages. We used GitHub Actions, Jest, and pnpm. We set up proper CI/CD practices, like avoiding deployment if the tests fail and caching dependencies.

A plus of GitHub Actions is that it brings the pipeline to the repo level, making it easier for developers to configure it. Now it is easier than ever to keep our app in a deployable state without depending so much on a DevOps team.

Happy coding!

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

No responses yet

Write a response