今回は、Next.jsを静的ページ(html)に出力する方法をメモしておきます
対象: Next.js 16.4.3
Next.jsプロジェクトを作成する
Next,jsプロジェクトの作成は以下のコマンドで作成します
npx create-next-app@latest
プロジェクトの項目を選択して作成を完了させます
デフォルトプロジェクトをbuildしてみる
はい、何も変更しない状態で静的ページを出力させたいと思います
npm run build
すると…
npm notice run learn_nextjs@0.1.0 build
npm notice run next build
▲ Next.js 16.4.2 (Turbopack)
✓ Running next.config.ts took 30ms
- Cache Components enabled
- Partial Prefetching enabled
- Experiments (use with caution):
✓ agentFeedback
Creating an optimized production build ...
✓ Compiled successfully in 203ms
Running TypeScript ...
Finished TypeScript in 1209ms ...
Collecting page data using 5 workers ...
✓ Collecting page data using 5 workers in 417ms
Generating static pages using 5 workers (0/4) ...
Generating static pages using 5 workers (1/4)
Generating static pages using 5 workers (2/4)
Generating static pages using 5 workers (3/4)
✓ Generating static pages using 5 workers (4/4) in 485ms
Finalizing page optimization ...
✓ Finalizing page optimization in 7ms
Route (app)
┌ ○ /
└ ○ /_not-found
○ (Static) prerendered as static content
正常に終了されて、staticファイルが生成しました的な表記になりますが、出力ディレクトリ(out)がありません
原因は出力設定がされていないためです
早速設定を行い出力を設定します
静的出力設定を行う
next.config.ts ファイルのデフォルト状態は以下です
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
experimental: {
agentFeedback: true,
},
cacheComponents: true,
partialPrefetching: true,
turbopack: {
rules: {
"*.css": {
loaders: ["@tailwindcss/turbopack"],
as: "*.css",
},
},
},
};
export default nextConfig;
この設定から静的ファイルの書き出し設定 output:”export” を追加します
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
output: 'export',
experimental: {
agentFeedback: true,
},
cacheComponents: true,
partialPrefetching: true,
turbopack: {
rules: {
"*.css": {
loaders: ["@tailwindcss/turbopack"],
as: "*.css",
},
},
},
};
export default nextConfig;
再度 buildを実行します…
npm notice run learn_nextjs@0.1.0 build
npm notice run next build
▲ Next.js 16.4.2 (Turbopack)
✓ Running next.config.ts took 31ms
- Cache Components enabled
- Partial Prefetching enabled
- Experiments (use with caution):
✓ agentFeedback
Creating an optimized production build ...
✓ Compiled successfully in 1189ms
Running TypeScript ...
Finished TypeScript in 1328ms ...
Collecting page data using 5 workers ...
✓ Collecting page data using 5 workers in 508ms
Generating static pages using 5 workers (0/4) ...
Generating static pages using 5 workers (1/4)
Generating static pages using 5 workers (2/4)
Generating static pages using 5 workers (3/4)
✓ Generating static pages using 5 workers (4/4) in 618ms
Finalizing page optimization ...
> Build error occurred
Error: Invariant: PPR cannot be enabled in export mode
at ignore-listed frames
はい、来ました。Buildエラーになります
Buildエラーの解消方法
デフォルトのプロジェクトではPPR(partialPrefetching)が有効になっています。
そのため出力できないよと言うことです。
partialPrefetchingってなんでしょう?
partialPrefetching は、アプリレベルで部分プリフェッチ(Partial Prefetching)を有効にします。フレームワークはデフォルトで各ルートの静的な部分をプリフェッチしますが、個々のリンクに prefetch={true} を設定すると、リンクごとのプリフェッチが使用され、より多くのデータを取得できます。
https://nextjs.org/docs/app/api-reference/config/next-config-js/partialPrefetching
要するに
リンクのプリフェッチされるようです。つまりは普通のhtmlでは使えない機能ですね
PPRを無効にしてbuildしてみる
先ほど調べたPPR(partialPrefetching)ですが、設定ファイル(next.config.ts)に明確にtrueになっています
それを無効にします
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
output: 'export',
experimental: {
agentFeedback: true,
},
cacheComponents: true,
partialPrefetching: false,
turbopack: {
rules: {
"*.css": {
loaders: ["@tailwindcss/turbopack"],
as: "*.css",
},
},
},
};
export default nextConfig;
実行してもPPRが有効だとエラーになります
Finalizing page optimization ...
> Build error occurred
Error: Invariant: PPR cannot be enabled in export mode
at ignore-listed frames
先ほどのと同じですね
問題はcacheComponentsです
Cache Components は、use cache ディレクティブを使って、コンポーネントレベルおよび関数レベルのキャッシュを可能にします。データ取得はデフォルトで動的であり、ページ、コンポーネント、または関数のレベルで何をキャッシュするかを自由に選べます。Next.js は静的な HTML シェルを事前にレンダリングして即座に配信し、動的なコンテンツは準備ができ次第ストリーミングされるため、1つのルート内で静的なコンテンツと動的なコンテンツを混在させることができます。
partialPrefetching は明示的に設定してください。未設定のままにすると警告がログに出力されます。これは、Cache Components の最初のリリースには Partial Prefetching が含まれていなかったためです。false に設定するのは、Partial Prefetching の登場前に Cache Components を導入したアプリを移行している間に限ってください。次のメジャーリリースでは、両方の機能がすべての環境で有効になり、これらのオプションは削除されます。
https://nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents
要するに
cacheComponentsはpartialPrefetchingが必要なわけです(build時にPPRが有効になるかな)
なので、明示的にこの二つの設定を無効にします
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
output: 'export',
experimental: {
agentFeedback: true,
},
cacheComponents: false,
partialPrefetching: false,
turbopack: {
rules: {
"*.css": {
loaders: ["@tailwindcss/turbopack"],
as: "*.css",
},
},
},
};
export default nextConfig;
buldを行うと正常に出力されたようです
npm notice run learn_nextjs@0.1.0 build
npm notice run next build
▲ Next.js 16.4.2 (Turbopack)
✓ Running next.config.ts took 32ms
- Experiments (use with caution):
✓ agentFeedback
Creating an optimized production build ...
✓ Compiled successfully in 1048ms
Running TypeScript ...
Finished TypeScript in 1275ms ...
Collecting page data using 5 workers ...
✓ Collecting page data using 5 workers in 430ms
Generating static pages using 5 workers (0/4) ...
Generating static pages using 5 workers (1/4)
Generating static pages using 5 workers (2/4)
Generating static pages using 5 workers (3/4)
✓ Generating static pages using 5 workers (4/4) in 477ms
Finalizing page optimization ...
✓ Finalizing page optimization in 531ms
Route (app)
┌ ○ /
└ ○ /_not-found
○ (Static) prerendered as static content
出力先は out ディレクトリ
正常にstatic contentが作成されると以下のディレクトリに出力されます
out
├── __next.__PAGE__.txt
├── __next._full.txt
├── __next._tree.txt
├── _next
│ ├── static
│ │ ├── chunks
│ │ ├── media
│ │ └── w9TrsHlx8onnPaS_SaRW2
│ └── w9TrsHlx8onnPaS_SaRW2
├── _not-found
│ ├── __next._full.txt
├── _not-found.html
├── _not-found.txt
├── 404.html
├── favicon.ico
├── file.svg
├── globe.svg
├── index.html
├── index.txt
├── next.svg
├── vercel.svg
└── window.svg
ざっくりこれだけ生成されます。
index.htmlをWebサーバにドキュメントルートとして表示するとNext.jsのページが表示されます
npx serve@latest out
npm notice run learn_nextjs@0.1.0 npx
npm notice run 'serve' out
│ Serving! │
│ - Local: http://localhost:58270 │
ブラウザでhttp://localhost:58270 にアクセスすると動作できました
その他
はい、このままでもOKですが、このままだとrootにある前提なのでサブディレクトリには展開できません
Build結果のソースをみると分かりますが、
<link rel="preload" href="/_next/static/media/797e433ab948586e-s.p.0r6juujl39pe6.woff2"
とPathが / 直下から見ます
サブディレクトリに静的ファイルを配置してもルートの_nextを参照するため動作しません
assetPrefixにてhrefのプリフィックスを指定する
assetPrefix: ‘.’,を指定する
assetPrefixを使用すると参照パスに./から始まることによりサブディレクトリからも参照できます
<link rel="preload" href="./_next/static/media/797e433ab948586e-s.p.0r6juujl39pe6.woff2"
https://nextjs.org/docs/app/api-reference/config/next-config-js/assetPrefix
basePathを指定する
basePathを設定するとサブディレクトリの指定できます
basePath: ‘/sample’,と指定すると以下です。
すでにリリースするサブディレクトリが決定している場合はこれが良い選択です
<link rel="preload" href="/sample/_next/static/media/797e433ab948586e-s.p.0r6juujl39pe6.woff2"
課題
これで十分だとは思いますが、まだ、課題があります
それはLinkの参照パスがルートのままなのです
https://www.mmpp.org/react_sample/uml
サンプルページを確認すると思いますが、Sampleのリンクを押下して umlディレクトリに参照させたいのですが、/umlに飛んでページが見つからない動作になっています
これが改善できれば静的ページができるぞ!って言えそうですね
終いに
今回はNext.jsをcreateプロジェクトした生状態で静的ファイル出力ができない問題と解消方法をメモしておきました
と言うのも、先のUML出力ページを参照していたのですが、静的ファイル出力を行う時に…
できない…
設定をいじっても…
ってかなり悩みました
試行錯誤するうちに、そもそもデフォルト状態のNext,jsは静的ファイルが出力できたよね…
から始まりました
今後は課題にあります。
- 静的ファイル出力するとuml.htmlが出力されている。しかし、index.htmlの内容では/umlを見に行くという問題も含まれています
この辺り、基本的な部分を考えたいですね