Speed Up Your Vite Builds with Caching in GitHub Actions
If your CI spends most of its time on yarn install for packages that haven't changed, caching is the cheapest speedup available. GitHub Actions ships a first-class cache action — the only real work is deciding what to cache and how to key it.
Here's the setup I use for a Vite + Yarn project.
⚙️ The workflow
The workflow file lives in .github/workflows/:
YAMLname: Build and Cache Vite on: push: branches: - main pull_request: branches: - main jobs: build: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up Node.js uses: actions/setup-node@v3 with: node-version: "18" # Specify your Node.js version - name: Cache Yarn modules uses: actions/cache@v3 with: path: | ~/.cache/yarn ~/.yarn/cache key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- - name: Install dependencies run: yarn install - name: Cache Vite build output uses: actions/cache@v3 with: path: | dist key: ${{ runner.os }}-vite-${{ hashFiles('**/vite.config.js') }} restore-keys: | ${{ runner.os }}-vite- - name: Build project run: yarn build - name: Deploy or run tests run: echo "Deploy or run tests here"
🔍 What each step does
- Checkout code — pulls the repository into the runner.
- Set up Node.js — installs the Node version the project targets.
- Cache Yarn modules — restores Yarn's cache directories, so
yarn installskips re-downloading packages that haven't changed. The key hashesyarn.lock, so the cache invalidates exactly when dependencies change. - Install dependencies — with a warm cache, this is mostly linking instead of downloading.
- Cache Vite build output — restores the previous
dist, keyed onvite.config.js. - Build project — runs the Vite build.
- Deploy or run tests — a placeholder for whatever your pipeline does after the build.
🔑 How the cache keys actually behave
Two details worth understanding instead of copy-pasting:
keyis the exact identity of a cache entry. Because it includeshashFiles('**/yarn.lock'), bumping any dependency produces a new key — you never restore a stale cache by accident.restore-keysis the fallback. When there's no exact match, Actions restores the closest existing cache that shares the prefix. After a lockfile change, most packages are already in that older cache, so only the delta gets downloaded.
One honest caveat: caching dist keyed on the Vite config only pays off when the build can genuinely reuse previous output — Vite still rebuilds sources that changed. In most projects, the Yarn cache is where nearly all the savings come from, so measure before assuming the build-output cache helps.
🔚 Conclusion
With both caches in place, a build with unchanged dependencies skips the download step almost entirely — on a typical project that's minutes saved on every push and pull request, for about twenty lines of YAML.
Happy coding 🚀