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
oryarn
) 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
oryarn
).
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.