Next.js 14(+pnpm)でGitHub Pagesホスティング(+カスタムドメイン)

Next.js 14(+pnpm)で作成したWebアプリケーションをGitHub Pagesにホスティング(+カスタムドメイン構成)した際のノウハウメモです(2024年9~10月現在)。

それぞれの対処からしばらく経ってしまい、記憶と気力が怪しいので補足のためしばらく随時更新するかもしれません。

Next.jsで作成したWebアプリケーションを静的サイト生成(SSG: Static Site Generation)する際の情報になります。

開発資産の修正点

app/layout.tsx

import "./globals.css";

以下に修正します。

import "@/app/globals.css"

修正しないと静的サイト生成したHTMLからCSS/JavaScriptへのリンクに”/<GitHubリポジトリ名>“が入ってしまうための対処になります。カスタムドメインを利用しない場合は修正不要と思われます。

next.config.mjs

import { readFileSync } from 'fs';
import { join } from 'path';

const packageJsonPath = join(process.cwd(), 'package.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));

const isDev = process.env.NODE_ENV === 'development';

const nextConfig = {
  output: isDev ? undefined : 'export',
  env: {
    APP_VERSION: packageJson.version,
  },
  experimental: {
    missingSuspenseWithCSRBailout: false,
  },
};

例示した内容には3つの要素が含まれています。

  • 静的サイト生成のための設定
    • 変数isDevの設定およびoutputディレクティブ
  • useSearchParams (next/navigation)を静的サイトで使用するための設定(Next.js 14時点)
    • experimental/missingSuspenseWithCSRBailoutディレクティブ
    • useSearchParams(クエリパラメタ参照)を実装で使用していないのであれば不要(Suspenseが効かないなど副作用があります)
  • package.jsonに記述されているversionをアプリ内から環境変数APP_VERSIONで参照するための設定
    • 上記以外の記述

静的サイト生成のための設定にフォーカスして説明しますと、output: 'export'が静的サイト生成をするために最低限必要な設定です。ただしこれだけですと、通常の開発時にも静的サイト生成が行われてデバッグしにくいため、開発時は静的サイト生成が行われないようにしています。環境変数NODE_ENVの値が'development'ならば開発ビルドと判断し、outputディレクティブでundefinedを指定するようにしています。

pnpm run buildを実行した場合はoutフォルダに静的サイトのビルド出力が行われますので、静的サイト生成された場合の内容を確認することもできます。ただし画像やCSS参照パスの違い等により画面表示が崩れることがありますので動作確認には向かないでしょう。

(Imageコンポーネントを利用している.tsx) ※カスタムドメインを使用しない場合

import nextConfig from '@/next.config.mjs'; // .mjsは省略できない
const BASE_PATH = nextConfig.basePath || '';
:
<Image src={`${BASE_PATH}/...`}

GitHub Pagesのようにデプロイ先がサーバルートでない(URLが”https://<GitHubアカウント名>.github.io/<リポジトリ名>/)場合、Imageコンポーネントのsrc属性のパスを調整する修飾設定を行う必要があります。上記のように設定するとBASE_PATHにリポジトリ名が設定されますので、実際に画像ファイルが存在するパスへの解決が行われます。

ただしカスタムドメインでデプロイ先がサーバルートとなる場合は逆に不一致となりますので、この修正は不要となります。

GitHub Actionsの構成

開発資産をGitHubリポジトリにpushした際に静的サイトが生成されるようにGitHub Actionsを構成します。GitHubによって提示されるActionsのテンプレート内容は時期により異なりますので、状況に応じて適宜対応する必要があります。以下の記述は2024年9月実施時点での内容になります。

GitHubリポジトリページ > Settings > Code and automation > Pages

GitHub Pages > Build and deployment > Source

プルダウンからGitHub Actionsを選択

Next.js“の”Configure“ボタンを選択(表示されていない場合はプルダウンの下にある”browse all workflows“リンクを選択し、”Next.js“を検索)

リポジトリ配下に.github/workflows/nextjs.ymlというYAMLファイルを作成するためのテンプレートが展開された編集画面が表示されます。エディタ中のbuildセクション配下で”name: Setup Node“のセクションを探し、配下の”node-version“を使用しているNode.jsのバージョンに合わせます。使用しているバージョンは開発環境でnode --versionコマンドにより確認することができます。またpnpmを使用している場合は配下のwithセクションに”cache-dependency-path: ./pnpm-lock.yaml“を追記する必要があります。

  - name: Setup Node
    uses: actions/setup-node@v4
    with:
      node-version: "20"
      cache: ${{ steps.detect-package-manager.outputs.manager }}
      cache-dependency-path: ./pnpm-lock.yaml

pnmpを使用している場合は”name: Detect package manager“のセクション配下にpnpmを検出するための条件分岐を追記する必要があります。package.jsonより先に検出される必要があるため、追記位置に注意します。

  - 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 }}/pnpm-lock.yaml" ]; then
        echo "manager=pnpm" >> $GITHUB_OUTPUT
        echo "command=install --frozen-lockfile" >> $GITHUB_OUTPUT
        echo "runner=pnpm" >> $GITHUB_OUTPUT
        npm install -g pnpm
        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 Pages“セクションのwith配下に”generator_config_file: next.config.mjs“を追記します。これに関連して同様の解説記事によっては”name: Static HTML export with Next.js“セクションをコメントアウトするように説明されていますが、2024年9月時点でのActionsテンプレートには存在しませんでしたので対応不要でした。

   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“セクションのwith配下の記述行に”**/pnpm-lock.yaml“を追記します(2か所)

  - 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', '**/pnpm-lock.yaml') }}-${{ 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', '**/pnpm-lock.yaml') }}-

Commit changes...“ボタンを選択し、以上の修正をコミットします。

GitHub Actionsの実行確認方法

mainブランチにpushするとActionsが実行されてビルド(静的サイト生成)が実施されます。

実行状況はリポジトリの”Actions“メニューで確認できます。

カスタムドメインの設定を行う前や、カスタムドメインの設定を行わない場合では、”https://<GitHubアカウント名>.github.io/<リポジトリ名>/“へアクセスすることにより生成された内容を参照することができます。ただしカスタムドメイン設定予定でImagebasePath修飾をしていない場合は画像が表示されないなど表示は崩れます。逆にカスタムドメインを行なわず(github.io/<リポジトリ名>/)にImagebasePath修飾をしたのに表示が崩れる場合はこれまでの設定のいずれかが誤っている可能性があります。

カスタムドメインの構成

生成した静的サイトをgithub.ioではなくカスタムドメインにする場合の方法の説明になります。以下でGitHub設定を行う前にDNS設定を行うと、第三者によるドメインの乗っ取りが行われるタイミングが生じるとのことで御注意ください。

カスタムドメインのためのGitHub設定

GitHubリポジトリページ > Settings > Code and automation > Pages

Build and deployment > Custom domain

入力欄にカスタムドメインを指定して”Save“ボタンを選択します。直後にDNSチェックが走り、少しするとエラー表示がされます。解説記事によっては設定したドメイン文字列が含まれる”CNAME“というファイルがリポジトリにコミットされ、カスタムドメインでの動作に必要で、GitHub Actionsによるデプロイではファイルが生成されないので別途対応が必要との情報もありましたが、自分の確認範囲ではファイルが無くても動作しました。

カスタムドメインのためのDNS設定

カスタムドメインがGitHub Pages(github.io)を指すようにDNS CNAMEレコードを構成します(以下DNSレコード設定のイメージ)。

<カスタムドメイン> CNAME <GitHubアカウント名>.github.io

DNS設定後のカスタムドメインのためのGitHub設定

DNS設定を行った後、GitHub設定に戻りCustom domainの”Check again“ボタンをクリックします。DNS設定の認識状況によりますので、時間の経過後のリトライが必要になる場合もあります。Check結果がOKになりますと、元のhttps://<GitHubアカウント名>.github.io/<リポジトリ名>/へのアクセスもカスタムドメインへリダイレクトされるようになります。

前述しましたようにGitHub Actionsでのビルド時にbasePathにリポジトリ名が設定されるのはカスタムドメイン設定後も同様ですが、カスタムドメインではアプリケーションルートがサーバルートとなり、github.ioでのようにリポジトリ名パスが不要ですので、basePath修飾は不要(修飾すると逆にパスが合わなくなる)になります。

HTTPS強制化

カスタムドメイン設定後、サーバ証明書が発行されるまで時間がかかりますので、”Enforce HTTPS“のチェックボックスが有効(クリック可能)になるまで待ち、有効化されたらチェックしてHTTPS強制化を行います。チェック後Custom domainでの表示が”DNS Check in Progress“にまたしばらくなるようです。

その他参考情報

静的サイト生成でのuseSearchParams(next/navigation)の使用

クエリパラメタを処理するためにuseSearchParamsを使用する場合、Next.js 14ではそのままでは静的サイト生成で動作しません。使用する場合は前述のようにnext.config.mjsに”missingSuspenseWithCSRBailout: false“を指定する必要があります(Next.js 14時点では実験的機能)。

ただしSuspenseが効かなくなるなどの副作用もあるようですので(詳細未確認)、注意して御利用ください。Next.js 15以降では正式に利用できるようになるかもしれないようです。

静的サイト生成ではRoute HandlersによるREST APIが(実用的)実装できない

正確にいうとRoute HandlersによるAPIエンドポイントは設けることができ、レスポンスも返すことができますが、クエリパラメタが処理できません。request.nextUrl.searchParamsでパラメタ値が取得できないため、パラメタ指定に応じたレスポンスを返すことができず、エンドポイントごとに固定のレスポンスとなるため、実用的な実装ができない状況になります。

Visual Studio Codeでのデバッグ(ステップ実行等)

.vscode/launch.jsonに以下の記述をすることにより、ローカル(localhost:3000)でのデバッグ実行ができ、VSCode上のブレークポイント設定やステップ実行、変数値参照等が行えます。自分が試した限りでは”Next.js: debug server-side“をまず実行し、”Next.js: debu client-side“をその後実行することでGoogle Chromeが起動されデバッグをすることができました。”Next.js: debug full stack“については正常動作を確認できていません。

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Next.js: debug server-side",
      "type": "node-terminal",
      "request": "launch",
      "command": "pnpm run dev"
    },
    {
      "name": "Next.js: debug client-side",
      "type": "pwa-chrome",
      "request": "launch",
      "url": "http://localhost:3000",
      "resolveSourceMapLocations": [
        "${workspaceFolder}/", "!/node_modules/", "!/.next/**"
      ]
    },
    {
      "name": "Next.js: debug full stack",
      "type": "node-terminal",
      "request": "launch",
      "command": "pnpm run dev",
      "console": "integratedTerminal",
      "serverReadyAction": {
        "pattern": "started server on .+, url: (https?://.+)",
        "uriFormat": "%s",
        "action": "debugWithChrome"
      }
    }
  ]
}

未解決の問題

  • app/layout.tsxのheadにGoogle Analyticsタグを記述しても静的サイト生成されたページではbodyに配置される。

適用例

これまでの情報を適用した例として手前味噌になりますがWebアプリケーション「Re:将棋thread」ソースリポジトリを示します。稚拙な実装ですが御参考まで。

https://github.com/bills-appworks/bsky-shogithread-replay

主な参考情報

おすすめ

1件の返信

  1. 2024年11月1日

    […] Next.js 14(+pnpm)でGitHub Pagesホスティング(+カスタムドメイン) […]

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です