You Should Publish Your Next.js App to GitHub Pages

Published on
Product Minting

Recently I revamped my open source version of 2048 Game and decided to migrate it to Next.js and React 18. The existing game was published to GitHub Pages without any custom domain. I was considering to deploy to Vercel but it would lose organic traffic from Google which was build up over the last 3 years. It means I needed to experiment with deployment to GitHub Pages and today I will share what i learnt.

If you want to see the end result of before reading the whole article, you can check it here.

Quick Intro

I will be using two GitHub features – GitHub Actions and GitHub Pages. If you haven't heard of them, let me quickly explain:

GitHub Actions are like little workflows that can do tasks on your projects. It's like having a helper that automatically does things you tell it to do. You can use Actions to run tests, for quality checks, or to build your application. In my case, I used this workflows to automate deployment pipeline.

What are GitHub Pages? Think of them like a web hosting option for developers and open source projects. You can use GitHub Pages to share your portfolios, host websites of your open-source projects, or just publish your pet projects like mine.

Now let's get started.

Step 1 – Activate GitHub Pages for Your Repository

To publish our Next.js application, I needed to activate GitHub Pages for the project’s repository. You can find in the Settings tab (1 in the image below), then select Pages from the menu on the left-hand side (2), and find the dropdown menu that allows us to specify the deployment Source (3).

Github Project Settings

Github Project Settings

Now you will need to change the deployment Source to GitHub Actions.

GitHub Pages Settings

GitHub Pages Settings

From now on, your project has a dedicated page. You only need to publish content there.

Step 2 – Configure the Next.js Build Process

Before deploying the Next.js app, it's important to change the build output. By default, Next.js uses Node.js to run the application, and this is incompatible with GitHub Pages.

GitHub Pages is designed to host static files, which means we can publish only HTML, CSS, JavaScript (and other static files) there. So we'll need to enable static page generation in Next.js.

To do so, you will change the output mode to export inside next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
  output: "export",  // <=== enables static exports
  reactStrictMode: true,

module.exports = nextConfig;

Now after running next build, Next.js will generate an out folder containing static assets for your app. In the next steps, we will take this directory and upload it to GitHub Pages.

Step 3 – Fix Missing Images

The Pages are published under a sub-path of a domain and takes the project name as a sub-path. Confusing? Let's take a sneak peak into a URL of my 2048 game as an example:

Github created a dedicated subdomain for my user called mateuszsokola (my username). But the project is published under the sub-path, which in my case is /2048-in-react. Unfortunately, this will lead to issues with missing images and styles.

By default, Next.js maps all static assets the domain. This means that the favicon.ico file will be resolved to instead of

To fix this, we can set up a path prefix by adding basePath inside the next.config.js file:

/** @type {import('next').NextConfig} */
const nextConfig = {
  basePath: "/2048-in-react", // <=== here it is
  output: "export",
  reactStrictMode: true,

module.exports = nextConfig;

In my case, it is /2048-in-react since my project is called 2048-in-react.
Remember to include the (/) at beginning of the project directory.

Step 4 – Create Github Actions

Next.js is producing deployment artifacts that can be published to GitHub Pages. Now it's due time to set up Github Actions to publish them. I decided the deployment into two separate actions to promote reusability:

The setup-node action will set up Node.js and instal all dependencies. Having a standalone action for the Node.js setup will allow me to reuse it for other pipelines. For example, I have pipelines that run code linter and tests. Probably you want to have more than one action as well.

The publish action will build Next.js artifacts and publish them to GitHub Pages each time we merge code into the main branch.

Let me begin by explaining the setup-node action. Here is the code:

# File: .github/workflows/setup-node/action.yml
name: setup-node
description: "Setup Node.js ⚙️ - Cache dependencies ⚡ - Install dependencies 🔧"
  using: "composite"
    - name: Setup Node.js ⚙️
      uses: actions/setup-node@v4
        node-version: 20

    - name: Cache dependencies ⚡
      id: cache_dependencies
      uses: actions/cache@v3
        path: node_modules
        key: node-modules-${{ hashFiles('package-lock.json') }}

    - name: Install dependencies 🔧
      shell: bash
      if: steps.cache_dependencies.outputs.cache-hit != 'true'
      run: npm ci

Important: Create this file in the  .github/workflows/setup-node directory in your project. Make sure to call it action.yml.

What does this snippet do?

  1. It creates a composite action. The composite action allows you to bundle multiple workflow steps into a single action. If it isn’t clear you will understand it once we get into the second action.

  2. It creates a new build environment using Node.js 20 and installs project dependencies.

These are the most important parts of the setup-node action. Now, let's move on to the publish action:

# File: .github/workflows/publish.yml
name: publish-to-github-pages
      - main

  contents: read
  pages: write
  id-token: write

  group: "pages"
  cancel-in-progress: false

    runs-on: ubuntu-latest

      - name: Checkout 🛎️
        uses: actions/checkout@v4

      - name: Setup Node.js ⚙️ - Cache dependencies ⚡ - Install dependencies 🔧
        uses: ./.github/workflows/setup-node

      - name: Setup Pages ⚙️
        uses: actions/configure-pages@v4
          static_site_generator: next

      - name: Build with Next.js 🏗️
        run: npx next build

      - name: Upload artifact 📡
        uses: actions/upload-pages-artifact@v3
          path: ./out

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

    runs-on: ubuntu-latest
    needs: build

      - name: Publish to GitHub Pages 🚀
        id: deployment
        uses: actions/deploy-pages@v4

Create this file in the .github/workflows directory in your project. You can name the file as you like – I called mine publish.yml.

What it does?

  1. This action is triggered each time the new code is pushed or merged into the main branch.

  2. It uses the setup-node action to set up the environment. The composite action I created in the previous action. Now you know how to include your composite actions in other actions.

  3. The action has two stages: in the first stage, Next.js app is being built. In the second stage, the artifacts from the first stage are uploaded to GitHub Pages.

These are the most important aspects of the deployment pipeline. I skipped the permissions and concurrency setup since they remain unchanged for all GitHub Pages deployments.

Now, action is ready to use.

Commit and Push

After committing and pushing your changes to the main branch, GitHub will automatically spin up the deployment to GitHub Pages.

To inspect the process, navigate to the Actions tab (1 in the image below), and select the publish-to-github-pages action from the menu on the left hand side (2). You will see a all your deployments on the screen (they are called workflows).

GitHub Actions - Workflows

GitHub Actions - Workflows

Now select the first one of those workflows, and you will see a two-stage deployment. In the deploy stage, you can find a link to your website on GitHub Pages.

GitHub Actions - Successful deployment

GitHub Actions - Successful deployment


Github Pages isn't sufficient for hosting websites with millions of views. But it's an perfect choice to host your portfolio or a website for your open-source project.

There are many free options to host our websites such as Vercel, but I wanted to show you an alternative. GitHub Pages is built for developers and I think every developer should be familiar with it.

If this article helped you, please share it on your social media.

And where do you deploy your application? Is it always Vercel?

Learn React 18 & Next.js

You don’t feel strong with React or Next.js? Join my online course on Udemy! I will help you to get started with React by creating a fully-functional 2048 Game. I believe creating games makes learning more fun, and you'll have something cool to show your friends.


🧑‍🎓 Join React 18 and Next.js course on Udemy – 80% OFF only this week.

Discussion (20)

Not yet any reply