unstable_runtimeJSを使ってNext.jsを静的化する

unstable_runtimeJSを使ってNext.jsを静的化する

Next.jsでSSGを行っていると<NextScript>が大量のコードを書き出すのが気になることがあります。

たとえば pages/index.js に以下のようなページを作成して、

export default function Home({text}) {
  return <h1>{text}</h1>
}
export async function getStaticProps() {
  return {
    props: {
      text: 'Hello World!'
    }
  }
}

npm run build && npx next exportコマンドを実行するとout/index.htmlとして以下のようなHTMLが出力されます。(長いのでbodyの中身だけ)

<div id="__next" data-reactroot="">
    <h1>Hello World!</h1>
</div>
<script id="__NEXT_DATA__" type="application/json">
{
    "props":{
        "pageProps":{
            "text":"Hello World!"
        },
        "__N_SSG":true
    },
    "page":"/",
    "query":{},
    "buildId":"RdT3qo4GDsmXmal46xOW0",
    "isFallback":false,
    "gsp":true,
    "scriptLoader":[]
}
</script>

冒頭のHTMLは良いとして、 __NEXT_DATA__として様々なデータがHTMLに出力されています。

これはNext.jsがSSGして出力するHTMLは実際にブラウザで描画されるものではなく、Pre-renderingと呼ばれる状態で、Next.jsはブラウザ表示時にこのHTMLをベースにhydration処理を行い、Nextアプリケーションとしての振る舞いを付与していきます。
Basic Features: Pages – Pre-rendering | Next.js

そのためにhydration処理に必要な情報が__NEXT_DATA__として出力されています。

Webサイトがアプリケーションとしての振る舞いが大きい場合には有効なのですが、メディアサイトといった静的サイトジェネレーターとして目的が大きい場合にはhydration処理が不要になることもあります。

また、大量のpropsをもとにページを生成している場合にはHTMLとは別に__NEXT_DATA__内にもpropsのデータが重複して出力されることになりHTMLが肥大化してしまうこともあります。

そういった場合にはpageコンポーネント内に以下の記述を行うことでhydration処理を行わないように振る舞いを変更することができます。

export const config = {
  unstable_runtimeJS: false
}

pages/index.jsにunstable_runtimeJSを追加した上でビルドすると以下のようなHTMLが出力されるようになります。

<div id="__next" data-reactroot="">
    <h1>Hello World!</h1>
</div>

読み込まれるJSファイルもhydration処理に必要な処理が読み込まれずにファイルサイズはかなり削減可能です。

問題点

一見良いこと尽くめのように見えますが、Nextアプリケーションとしての振る舞わない単なるHTMLとなってしまったので、リンク先のプリフェッチと行ったNext.jsの提供する機能の恩恵を受けられなくなります。

また useStateuseEffectといったReactアプリケーションとしての機能も実行されなくなりますのでJavaScriptによる機能追加を行いたい場合は、Nextアプリケーションとは別にJavaScriptを記述する必要があります。

ある程度のscript量がある場合には、Next / Reactが提供する体験から外れるのは辛いものがありますので、そういったケースでは利用しないほうがよいでしょう。

また、unstable_runtimeJSの設定はオフィシャルのドキュメントにはない設定のためNext.jsのアップデート等で使えなくなる可能性があります。そういったリスクも考慮する必要があるでしょう。