🐱

Next.js 14.1 を GitHub Pages にデプロイするガイド

2024/03/25に公開

Next.jsで作成したプロジェクトは、
GitHub Actionsを使って、GitHub上にデプロイすることができます。

この記事では、GitHub Pagesにデプロイした手順と、遭遇したエラーについてまとめました!

検証環境:
・Next.js v14.1.3/ App Router

vercelの無料枠は、接続可能なGitHubリポジトリの数が3つまでなので、柔軟に使い分けできると良さそうですね!

1:Next.js/App Routerプロジェクトの作成

まずは、公式のinstallationに従い、以下のコマンドでNext.jsをインストールします。

npx create-next-app@latest

今回は、App Routerを使用し、
質問への回答はすべてデフォルトのまま進みます。

2:GitHub Pagesへのデプロイに必要な変更を加える

GitHubにプッシュする前に、2つ変更を加える必要があります!

①nextConfigファイルの変更

Next.js v14.1.3では、create-next-appしたら、
設定ファイルは、next.config.mjsになっているはずです。

nextConfigに、静的エクスポートの設定を記述します!

next.config.mjs
/**
 * @type {import('next').NextConfig}
 */
const nextConfig = {
+ output: "export",
}

export default nextConfig
ポイント:もし next.config.js を使う場合

nextConfigの設定は以下のようになります!

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: "export",
}

module.exports = nextConfig

設定ファイルが、next.config.jsか、next.config.mjsかによって、
後述する変更が異なるので注意してください!

自分は、これに詰まりました。。

特に理由がなければ、デフォルトのnext.config.mjsでOKだと思います!

これにより、next buildを実行すると、
./outフォルダが生成され、静的サイトが出力されます!

メモ:next exportは非推奨→削除

ちなみに、
以前のPages Routerでは静的サイトを出力するには next exportコマンドを実行する必要がありました。

これは、v13.3.0で非推奨になり、nextConfigにoutput: "export"の記述をする方法に置き換わりました。
そして、v14.0.0にて、next exportは削除されています!

詳しくは、下記のバージョン履歴をチェックしてみてください:
https://nextjs.org/docs/app/building-your-application/deploying/static-exports#version-history

②ImageコンポーネントにbasePathの設定を記載する

GitHub Pagesにデプロイする場合、URLは以下のようになります:
https://[アカウント名].github.io/[リポジトリ名]/

このようなURLの場合、Next.jsで、basePathの設定を行うことができます!

https://nextjs.org/docs/app/api-reference/next-config-js/basePath

ドメインのサブパスの下にNext.jsアプリケーションをデプロイするには、basePath設定オプションを使用できます。

実は、後述するGitHub Actionsのワークフローファイルで、
basePathnextConfig内に、自動で追加する処理が含まれています。

なので、今回は自分でnextConfig内に、basePathを設定する必要はありません!

ただし、Imageコンポーネントを使用する場合は、
上記のnextConfig内の設定とは別で、src属性にbasePathを追加する必要があります。

app/page.tsx内で、デフォルトで記載されているImageコンポーネントを、以下のように書き換えます!

import Image from 'next/image'

+ import nextConfig from "../../next.config.mjs";
+ const BASE_PATH = nextConfig.basePath || "";

    ...
    <Image
+     src={`${BASE_PATH}/vercel.svg`}
      alt="Vercel Logo"
      className="dark:invert"
      width={100}
      height={24}
      priority
    />
    ...
    <Image
      className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
+     src={`${BASE_PATH}/next.svg`}
      alt="Next.js Logo"
      width={180}
      height={37}
      priority
    />

ポイント:もし next.config.js を使う場合

上記のコードを、next.config.jsファイルからのインポートに合わせる必要があります!

import nextConfig from "../../next.config.mjs";
const BASE_PATH = nextConfig.basePath || "";

import { basePath } from "../../next.config.js";
const BASE_PATH = basePath || "";

これによって、Imageコンポーネントを使った画像が、適切に提供されます!

3:GitHub上にプッシュする

これに関しては、そのままですね!
コミットして、GitHub上にプッシュします!

GitHub Actionsのテンプレートに関しては、GitHub上で導入・編集できます!

4:GitHub Actionsのテンプレートを導入・編集

GitHub Pagesにデプロイするにあたって、
公式から提供されている、Next.js用のGitHub Actionsのテンプレートを利用できます

これによって、mainブランチへのプッシュをトリガーに、
ビルドとデプロイのプロセスを、自動化することが可能です!

以下の画像のように、リポジトリの画面から、
Settings > Pages > Next.jsテンプレートの「Configure」と進みます:

すると、Next.js用のGitHub Actionsのテンプレートファイル(nextjs.yml)の、編集画面が表示されます。

これは、現時点(2024/03/25)では、最新のNext.jsに合わせて更新されていないので、
少し変更を加える必要があります!

デフォルトの通りnext.config.mjsを使っている場合、
以下のように、ハイライトされている2箇所を書き換えて、コミットします!

# 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 pushes targeting the default branch
  push:
    branches: ["main"]

  # 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@v4
        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
+         generator_config_file: next.config.mjs
      - 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: Static HTML export with Next.js
-     #   run: ${{ steps.detect-package-manager.outputs.runner }} next export
      - 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

もし、next.config.jsを使っている場合は、
下記の「1つ目の変更」を加える必要はありません!

変更点①:generator_config_file: next.config.mjs

Setup Pagesの中に、
generator_config_file: next.config.mjsを追加しています!

      - name: Setup Pages
        uses: actions/configure-pages@v4
        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
+         generator_config_file: next.config.mjs

この、Setup Pagesには、basePathnextConfig内に、自動で追加する処理が含まれています。

つまり、ここの部分で、
実際のデプロイ先のURLhttps://[アカウント名].github.io/[リポジトリ名]/を追加しています!

その際、注意すべきポイントが、generator_config_file: next.config.mjsを指定しない場合、
標準でnext.config.jsファイルを設定ファイルとして検出するようになっています

next.config.mjsを使用しているのに、上記の記載を追加しない場合、
next.config.jsファイルが新たに作成されてしまい、エラーがスローされます!

変更点②:next export

先述した通り、App Routerではnext exportコマンドが不要なため、この2行は削除 or コメントアウトします!

-     # - name: Static HTML export with Next.js
-     #   run: ${{ steps.detect-package-manager.outputs.runner }} next export

5:GitHub Pagesにデプロイ🚀

mainブランチへコミットを行うと、自動的にGitHub Actionsが作動するので、
デプロイが完了するのを少し待ったら、完了になります👍


詰まったポイント)Error: Process completed with exit code 2.

Github Actionsのテンプレを使用して、デプロイする際に、以下のようなエラーに遭遇しました:

Run actions/upload-pages-artifact@v3
Run echo ::group::Archive artifact
Archive artifact
  tar: out: Cannot open: No such file or directory
  tar: Error is not recoverable: exiting now
  Error: Process completed with exit code 2.

このエラーの内容は、
./out ディレクトリ(静的エクスポートされたディレクトリ)が見つからない、ということです。
つまり、next.config.mjsに記述した、output: 'export',が読み込まれていないということになります。

結論として、これは、Github Actions テンプレのワークフローのSetup Pagesが原因でした。。

そのSetup Pagesの箇所でやっていることは、
basePath(デプロイ先のPagesのURL)を、nextConfig内に自動で挿入してくれるという処理です。

該当のActionsのソースコードを見に行って、原因がわかりました:
https://github.com/actions/configure-pages
(上記よりコードを引用)

    case 'next':
      // Next does not want a trailing slash
      path = removeTrailingSlash(path)

      return {
        configurationFile: generatorConfigFile || './next.config.js',
        blankConfigurationFile: `${__dirname}/blank-configurations/next.js`,
        properties: {
          // Configure a base path
          basePath: path,

          // Disable server side image optimization too
          // https://nextjs.org/docs/api-reference/next/image#unoptimized
          'experimental.images.unoptimized': true,
          // No longer experimental as of Next.js v12.3.0
          'images.unoptimized': true
        }
      }

上記では、Next.jsの設定ファイルに関して、
generatorConfigFile、または./next.config.jsとなっています。

generatorConfigFileに関しては、別ファイルにて、generator_config_fileの値を読み取っていました。

(メモ:Actionsの入力は、core.getInputで読み取ることができます。詳しくは下記を参照)
https://github.com/actions/toolkit/tree/master/packages/core

つまり、Setup Pages内部は次のような処理になっていました:

  • generator_config_fileが指定されていれば、それを設定ファイルとして検出し、そこにbasePathに関する情報を書き込む。
  • もし、何も指定されていなければ、./next.config.jsに書き込む。

create-next-appでは、next.config.mjsがデフォルトで作成されます。

なので、next.config.mjs の場合は、下記のオプションを指定しないといけません。

generator_config_file: next.config.mjs

この指定をしないと、next.config.mjsがあるのに、next.config.jsが作成されてしまい、元のnext.config.mjsの内容が反映されなくなります!

テンプレを使う際は、ちゃんと調べることが大切ですね!

おわりに

最後まで読んでいただきありがとうございます🐸

現時点(Next.js V14.1.3)では、create-next-appで、next.config.mjsがデフォルトで作成されるので、Actionsのテンプレートの変更には、注意が必要ですね!

気がついたら結構長くなっていましたが、
ぜひ、GitHub Pagesでのデプロイを試してみてください!

Happy Hacking !

参考

https://zenn.dev/pino0701/articles/nextjs_github_pages
https://stackoverflow.com/questions/77933974/how-to-deploy-nextjs-app-with-githup-actions

Discussion