Next.js 9.5のアップデートにより、GitHub Pagesで妥協しない動的ルーティングが可能になった


Qiita: Next.jsで作ったアプリをGitHub Pagesにデプロイする@ozaki25』や、『Qiita: Next.jsをGitHub Pagesにデプロイしたらリンクが壊れた』、『GitHub: gh-pages Hello World example』にNext.jsのWebサイトをGithub Pagesに投稿する方法が載っていますが、どれも静的ページのみへのルーティングを説明していて、動的ルーティングには深く言及されていません。

そこで、Next.js 9.4でサイトをGitHub Pagesにあげたところ、動的ルーティングでハマってしまいました。結果、対処法がわからずにVercelに移行したのですが、Next.js 9.5のアップデートベースパスをカスタマイズできる機能が追加され、自分がハマっていた動的ルーティングが解決しました!🎉

この記事では、Next.js 9.4の動的ルーティングのハマっていた背景とNext.js 9.5での対処法について説明します。

Next.js 9.4 × GitHub Pages 🤔

ご存知だと思いますが、GitHub Pagesはhttps://[username].github.io/[repository-name]といったurlにマッピングされます。これによって、以下のような普通のNext.jsのルーティングでは、https://[username].github.io/[repository-name]/itemsに行ってほしいのに、https://[username].github.io/itemsにルーティングされてしまい、リロード時にエラーとなってしまいます。

MyLink.tsx
const MyLink = () => (
  <Link href="/items">
    <a>Item</a>
  </Link>
)

なので、『Qiita: Next.jsをGitHub Pagesにデプロイしたらリンクが壊れた』などに記載されている変更が必要となります。

next.config.js
module.exports = {
  assetPrefix: process.env.NODE_ENV === 'production' ? '/[repository-name]' : ''
}
MyLink.tsx
const withRootPath = (path: string) =>
  process.env.NODE_ENV === 'production' ? `/[repository-name]/${path}` : path

const MyLink = () => (
  <Link href={withRootPath("/items")}>
    <a>Item</a>
  </Link>
)

これで、静的なリンクへはうまくいきます。が、前述している通り、動的ルーティングの場合は同様に以下のようにしてもプリフェッチされず、ブラウザのロードが挟まれてしまいます。

MyLink.tsx
const withRootPath = (path: string) =>
  process.env.NODE_ENV === 'production' ? `/[repository-name]/${path}` : path

const MyLink = () => (
  <Link href={withRootPath("/items")}>
    <a>Item</a>
  </Link>
)
const MyDynamicLink = () => (
  <Link href={withRootPath("/items/[id]")} as={withRootPath("/items/a")}>
    <a>Item</a>
  </Link>
)

また、上の記事に記載されている以下の方法で実装すると、エラーを出力します。

MyLink.tsx(差分)
const MyDynamicLink = () => (
  <Link href="/items/[id]" as={withRootPath("/items/a")}>
    <a>Item</a>
  </Link>
)

これは、hrefasが対応していない事によるエラーです。詳しい内容はNext.jsのリポジトリに記入してあります。vercel/next.js-Incompatible href and as values

…ということで、自分の知る限りではGitHubでNext.jsの動的ルーティングを実装するには、プリフェッチとブラウザへのロードを妥協するしかありませんでした。

Next.js 9.5 × GitHub Pages 🎉

最初に説明している通り、Next.js 9.5でベースパスを設定できるようになりました。対処法も簡単で、next.config.jsに追加するだけでOKです。

next.config.js
module.exports = {
  basePath: process.env.NODE_ENV === 'production' ? '/[repository-name]' : ''
}

これだけで、妥協しない動的ルーティングが可能になります🎉

おわりに

自分の認識不足で、Next.js 9.4でも動的ルーティングでプリフェッチできる方法がありましたら、教えてください。

デモとして使用したリポジトリ
- https://github.com/syakoo/next_gh-pages