Deploying Next.js to GitHub Pages: Use GitHub Actions and Custom Domains

Cover Image for Deploying Next.js to GitHub Pages: Use GitHub Actions and Custom Domains

Deploying a Next.js application to GitHub Pages can be a smooth process when combined with the automation capabilities of GitHub Actions. This comprehensive guide will explain every part of the workflow, ensuring you have a clear understanding of each step.

Optionally, We'll also cover how to set up a custom domain for your site.

Prerequisites

Before you start, ensure you have:

  • A GitHub repository containing your Next.js project.

  • Access to a custom domain, if you wish to use one (optional)

  • Basic familiarity with GitHub Actions and Next.js.

Setting Up the GitHub Actions Workflow

Create a file named main.yml in the .github/workflows/ directory of your repository. This file will define the workflow for deploying your Next.js app.

name: Deploy Next.js site to Pages

Workflow Trigger

on:
  push:
    branches:
      - '**'
    paths:
      - '**'
  workflow_dispatch:
  • push: This trigger ensures the workflow runs whenever you push changes to any branch or path in the repository. It keeps your site up-to-date with every commit.

  • workflow_dispatch: Allows manual triggering of the workflow from the GitHub Actions interface, giving flexibility for testing or redeployment.

Permissions

permissions:
  contents: read
  pages: write
  id-token: write
  • contents: read: Grants read access to the repository contents.

  • pages: write: Enables writing to the GitHub Pages site.

  • id-token: write: Necessary for the deployment process.

Concurrency

concurrency:
  group: "pages"
  cancel-in-progress: false
  • group: Groups all jobs into a concurrency group named "pages," ensuring only one deployment happens at a time.

  • cancel-in-progress: Set to false to allow ongoing deployments to complete before new ones start, preventing partial deployments.

Build Job

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
  • runs-on: Specifies the OS environment (ubuntu-latest) on which the job will run.

  • Checkout: Uses the actions/checkout action to clone the repository code into the workflow.

Absolutely! Here's a more detailed, section-by-section explanation of your GitHub Actions workflow, focusing on explaining each part thoroughly to make it easy to understand.

Detect Package Manager

      - name: Detect package manager
        id: detect-package-manager
        run: |
          if [ -f "${{ github.workspace }}/yarn.lock" ]; then
            echo "manager=yarn" >> $GITHUB_OUTPUT
            echo "command=install" >> $GITHUB_OUTPUT
            echo "runner=yarn" >> $GITHUB_OUTPUT
            exit 0
          elif [ -f "${{ github.workspace }}/package.json" ]; then
            echo "manager=npm" >> $GITHUB_OUTPUT
            echo "command=ci" >> $GITHUB_OUTPUT
            echo "runner=npx --no-install" >> $GITHUB_OUTPUT
            exit 0
          else
            echo "Unable to determine package manager"
            exit 1
          fi

This step detects whether to use yarn or npm based on the presence of yarn.lock or package.json. It sets appropriate variables for later steps.

Setup Node.js

yamlCopyEdit      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: ${{ steps.detect-package-manager.outputs.manager }}
  • Setup Node: Configures Node.js environment, caching the package manager (npm or yarn) for faster builds.

Configure Pages for Next.js

yamlCopyEdit      - name: Setup Pages
        uses: actions/configure-pages@v5
        with:
          static_site_generator: next
  • Setup Pages: Configures GitHub Pages for Next.js, automatically handling the base path and disabling server-side image optimization.

Restore Cache

yamlCopyEdit      - name: Restore cache
        uses: actions/cache@v4
        with:
          path: |
            .next/cache
          key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
          restore-keys: |
            ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-
  • Restore Cache: Uses actions/cache to speed up builds by caching the .next folder. Cache keys are based on OS, lock files, and source files to ensure accurate restorations.

Install Dependencies

yamlCopyEdit      - name: Install dependencies
        run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
  • Install Dependencies: Installs project dependencies using the detected package manager.

Build Next.js App

yamlCopyEdit      - name: Build with Next.js
        run: ${{ steps.detect-package-manager.outputs.runner }} next build
  • Build with Next.js: Runs the build command for Next.js using the appropriate runner (npx or yarn).

Upload Artifact

yamlCopyEdit      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: ./out
  • Upload Artifact: Uploads the build output to be used in the deployment job.

Deployment Job

yamlCopyEdit  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4
  • Deploy to GitHub Pages: Deploys the uploaded artifacts to GitHub Pages using actions/deploy-pages.

Configuring a Custom Domain (optional)

To set up a custom domain, create a CNAME file in your project's public folder with your domain name (e.g., www.yourdomain.com). GitHub Pages will use this file to link your custom domain to the site.

Enabling GitHub Pages

In your GitHub repository, navigate to Settings > Pages and select GitHub Actions as the source for your Pages site. This step finalizes the connection between the workflow and GitHub Pages.

my Next.js portfolio GitHub actions code for example -

visit my portfolio —- remiel.fyi


# Sample workflow for building and deploying a Next.js site to GitHub Pages
#
# To get started with Next.js see: https://nextjs.org/docs/getting-started

name: Deploy Next.js site to Pages

on:
  # Runs on all pushes to any branch
  push:
    branches:
      - '**' # Match any branch
    paths:
      - '**' # Match any file change

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  # Build job
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Detect package manager
        id: detect-package-manager
        run: |
          if [ -f "${{ github.workspace }}/yarn.lock" ]; then
            echo "manager=yarn" >> $GITHUB_OUTPUT
            echo "command=install" >> $GITHUB_OUTPUT
            echo "runner=yarn" >> $GITHUB_OUTPUT
            exit 0
          elif [ -f "${{ github.workspace }}/package.json" ]; then
            echo "manager=npm" >> $GITHUB_OUTPUT
            echo "command=ci" >> $GITHUB_OUTPUT
            echo "runner=npx --no-install" >> $GITHUB_OUTPUT
            exit 0
          else
            echo "Unable to determine package manager"
            exit 1
          fi
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: ${{ steps.detect-package-manager.outputs.manager }}
      - name: Setup Pages
        uses: actions/configure-pages@v5
        with:
          # Automatically inject basePath in your Next.js configuration file and disable
          # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized).
          #
          # You may remove this line if you want to manage the configuration yourself.
          static_site_generator: next
      - name: Restore cache
        uses: actions/cache@v4
        with:
          path: |
            .next/cache
          # Generate a new cache whenever packages or source files change.
          key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
          # If source files changed but packages didn't, rebuild from a prior cache.
          restore-keys: |
            ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-
      - name: Install dependencies
        run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
      - name: Build with Next.js
        run: ${{ steps.detect-package-manager.outputs.runner }} next build
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: ./out

  # Deployment job
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4
view rawmain.yml hosted with  by GitHub

Conclusion

By following this guide, you’ve set up a fully automated pipeline that builds and deploys your Next.js app to GitHub Pages, including the optional use of a custom domain. This approach simplifies the deployment process, ensuring your site is always up-to-date with the latest changes.

I’ve used this exact setup to deploy my own Next.js portfolio through GitHub Pages, and it works seamlessly, providing a professional and reliable way to showcase my work online.

💡
visit my portfolio —- remiel.fyi