静的エクスポート
Next.jsでは静的サイトやシングルページアプリケーション (SPA) として開始し、後からサーバーが必要な機能にオプションでアップグレードできます。
next build
を実行すると、Next.jsはルートごとにHTMLファイルを生成します。厳密なSPAを個別のHTMLファイルに分割することで、Next.jsはクライアントサイドで不要なJavaScriptコードの読み込みを回避し、バンドルサイズを削減してページ読み込みを高速化できます。
Next.jsはこの静的エクスポートをサポートしているため、HTML/CSS/JS静的アセットを提供できる任意のWebサーバーにデプロイしてホストできます。
設定
静的エクスポートを有効にするには、next.config.js
内で出力モードを変更します:
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
output: 'export',
// オプション: リンク `/me` -> `/me/` に変更し、`/me.html` -> `/me/index.html` を生成
// trailingSlash: true,
// オプション: 自動的な `/me` -> `/me/` 変換を防止し、`href`を保持
// skipTrailingSlashRedirect: true,
// オプション: 出力ディレクトリを `out` -> `dist` に変更
// distDir: 'dist',
}
module.exports = nextConfig
next build
を実行後、Next.jsはアプリケーションのHTML/CSS/JSアセットを含むout
フォルダを生成します。
サポートされる機能
Next.jsのコアは静的エクスポートをサポートするように設計されています。
サーバーコンポーネント
next build
を実行して静的エクスポートを生成する場合、app
ディレクトリ内で使用されるサーバーコンポーネントはビルド中に実行され、従来の静的サイト生成と同様に動作します。
結果のコンポーネントは、初期ページ読み込み用の静的HTMLと、ルート間のクライアントナビゲーション用の静的ペイロードとしてレンダリングされます。動的サーバー関数を使用しない限り、静的エクスポートを使用する際にサーバーコンポーネントを変更する必要はありません。
export default async function Page() {
// このfetchは`next build`時にサーバーで実行されます
const res = await fetch('https://api.example.com/...')
const data = await res.json()
return <main>...</main>
}
クライアントコンポーネント
クライアント側でデータ取得を行いたい場合は、SWRを使用したクライアントコンポーネントでリクエストをメモ化できます。
'use client'
import useSWR from 'swr'
const fetcher = (url: string) => fetch(url).then((r) => r.json())
export default function Page() {
const { data, error } = useSWR(
`https://jsonplaceholder.typicode.com/posts/1`,
fetcher
)
if (error) return '読み込みに失敗しました'
if (!data) return '読み込み中...'
return data.title
}
'use client'
import useSWR from 'swr'
const fetcher = (url) => fetch(url).then((r) => r.json())
export default function Page() {
const { data, error } = useSWR(
`https://jsonplaceholder.typicode.com/posts/1`,
fetcher
)
if (error) return '読み込みに失敗しました'
if (!data) return '読み込み中...'
return data.title
}
ルート遷移はクライアントサイドで行われるため、これは従来のSPAのように動作します。例えば、次のインデックスルートではクライアント側で異なる投稿にナビゲートできます:
import Link from 'next/link'
export default function Page() {
return (
<>
<h1>インデックスページ</h1>
<hr />
<ul>
<li>
<Link href="/post/1">投稿1</Link>
</li>
<li>
<Link href="/post/2">投稿2</Link>
</li>
</ul>
</>
)
}
import Link from 'next/link'
export default function Page() {
return (
<>
<h1>インデックスページ</h1>
<p>
<Link href="/other">他のページ</Link>
</p>
</>
)
}
画像最適化
next/image
による画像最適化は、next.config.js
でカスタム画像ローダーを定義することで静的エクスポートで使用できます。例えば、Cloudinaryのようなサービスで画像を最適化できます:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
images: {
loader: 'custom',
loaderFile: './my-loader.ts',
},
}
module.exports = nextConfig
このカスタムローダーは、リモートソースから画像を取得する方法を定義します。例えば、次のローダーはCloudinaryのURLを構築します:
export default function cloudinaryLoader({
src,
width,
quality,
}: {
src: string
width: number
quality?: number
}) {
const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality || 'auto'}`]
return `https://res.cloudinary.com/demo/image/upload/${params.join(
','
)}${src}`
}
export default function cloudinaryLoader({ src, width, quality }) {
const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality || 'auto'}`]
return `https://res.cloudinary.com/demo/image/upload/${params.join(
','
)}${src}`
}
その後、アプリケーションでnext/image
を使用し、Cloudinary内の画像への相対パスを定義できます:
import Image from 'next/image'
export default function Page() {
return <Image alt="カメ" src="/turtles.jpg" width={300} height={300} />
}
import Image from 'next/image'
export default function Page() {
return <Image alt="カメ" src="/turtles.jpg" width={300} height={300} />
}
ルートハンドラー
ルートハンドラーは、next build
実行時に静的レスポンスをレンダリングします。サポートされるHTTP動詞はGET
のみです。これはキャッシュされたデータや非キャッシュデータから静的HTML、JSON、TXT、その他のファイルを生成するために使用できます。例えば:
export async function GET() {
return Response.json({ name: 'Lee' })
}
export async function GET() {
return Response.json({ name: 'Lee' })
}
上記のファイルapp/data.json/route.ts
は、next build
中に静的ファイルとしてレンダリングされ、{ name: 'Lee' }
を含むdata.json
を生成します。
受信リクエストから動的な値を読み取る必要がある場合は、静的エクスポートを使用できません。
ブラウザAPI
クライアントコンポーネントはnext build
中にHTMLにプリレンダリングされます。Web API(window
、localStorage
、navigator
など)はサーバーで利用できないため、これらのAPIに安全にアクセスするにはブラウザで実行されている場合のみにする必要があります。例えば:
'use client';
import { useEffect } from 'react';
export default function ClientComponent() {
useEffect(() => {
// ここで`window`にアクセスできます
console.log(window.innerHeight);
}, [])
return ...;
}
サポートされない機能
Node.jsサーバーを必要とする機能や、ビルドプロセス中に計算できない動的ロジックはサポートされていません:
dynamicParams: true
の動的ルートgenerateStaticParams()
なしの動的ルート- Requestに依存するルートハンドラー
- クッキー
- リライト
- リダイレクト
- ヘッダー
- ミドルウェア
- 増分的静的再生成
- デフォルト
loader
の画像最適化 - ドラフトモード
これらの機能をnext dev
で使用しようとすると、ルートレイアウトでdynamic
オプションをerror
に設定した場合と同様のエラーが発生します。
export const dynamic = 'error'
デプロイ
静的エクスポートでは、Next.jsはHTML/CSS/JS静的アセットを提供できる任意のWebサーバーにデプロイしてホストできます。
next build
を実行すると、Next.jsは静的エクスポートをout
フォルダに生成します。next export
はもはや必要ありません。例えば、次のルートがあるとします:
/
/blog/[id]
next build
実行後、Next.jsは次のファイルを生成します:
/out/index.html
/out/404.html
/out/blog/post-1.html
/out/blog/post-2.html
Nginxのような静的ホストを使用している場合、受信リクエストから正しいファイルへのリライトを設定できます:
server {
listen 80;
server_name acme.com;
root /var/www/out;
location / {
try_files $uri $uri.html $uri/ =404;
}
# `trailingSlash: false`の場合に必要
# `trailingSlash: true`の場合は省略可能
location /blog/ {
rewrite ^/blog/(.*)$ /blog/$1.html break;
}
error_page 404 /404.html;
location = /404.html {
internal;
}
}
バージョン履歴
バージョン | 変更内容 |
---|---|
v13.4.0 | App Router(安定版)が強化された静的エクスポートサポートを追加、Reactサーバーコンポーネントとルートハンドラーの使用を含む |
v13.3.0 | next export が非推奨となり、"output": "export" に置き換えられました |