Next.jsアプリケーションを静的エクスポートする方法

Next.jsでは、静的サイトまたはシングルページアプリケーション(SPA)として開始し、後でサーバーが必要な機能を使用するようにアップグレードすることが可能です。

next buildを実行すると、Next.jsは各ルートごとにHTMLファイルを生成します。厳密なSPAを個別のHTMLファイルに分割することで、Next.jsはクライアントサイドで不要なJavaScriptコードの読み込みを避け、バンドルサイズを削減し、ページの読み込みを高速化できます。

Next.jsはこの静的エクスポートをサポートしているため、HTML/CSS/JSの静的アセットを提供できる任意のWebサーバーにデプロイしてホストできます。

設定

静的エクスポートを有効にするには、next.config.js内のoutputモードを変更します:

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 'Failed to load'
  if (!data) return 'Loading...'

  return data.title
}

ルート遷移はクライアント側で行われるため、これは従来のSPAのように動作します。例えば、次のインデックスルートでは、クライアント側で異なる投稿にナビゲートできます:

import Link from 'next/link'

export default function Page() {
  return (
    <>
      <h1>Index Page</h1>
      <hr />
      <ul>
        <li>
          <Link href="/post/1">Post 1</Link>
        </li>
        <li>
          <Link href="/post/2">Post 2</Link>
        </li>
      </ul>
    </>
  )
}

画像最適化

next/imageによる画像最適化は、next.config.jsでカスタム画像ローダーを定義することで静的エクスポートと共に使用できます。例えば、Cloudinaryのようなサービスで画像を最適化できます:

next.config.js
/** @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}`
}

その後、アプリケーションでnext/imageを使用し、Cloudinary内の画像への相対パスを定義できます:

import Image from 'next/image'

export default function Page() {
  return <Image alt="turtles" src="/turtles.jpg" width={300} height={300} />
}

ルートハンドラ

ルートハンドラは、next buildを実行すると静的レスポンスをレンダリングします。サポートされるHTTP動詞はGETのみです。これは、キャッシュされたデータまたは非キャッシュデータから静的HTML、JSON、TXT、またはその他のファイルを生成するために使用できます。例えば:

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 APIwindowlocalStoragenavigatorなど)はサーバーで利用できないため、ブラウザで実行されている場合にのみこれらのAPIに安全にアクセスする必要があります。例えば:

'use client';

import { useEffect } from 'react';

export default function ClientComponent() {
  useEffect(() => {
    // ここで`window`にアクセスできます
    console.log(window.innerHeight);
  }, [])

  return ...;
}

サポートされない機能

Node.jsサーバーを必要とする機能、またはビルドプロセス中に計算できない動的ロジックはサポートされていません

これらの機能をnext devで使用しようとすると、ルートレイアウトでdynamicオプションをerrorに設定した場合と同様のエラーが発生します。

export const dynamic = 'error'

デプロイ

静的エクスポートを使用すると、Next.jsはHTML/CSS/JS静的アセットを提供できる任意のWebサーバーにデプロイしてホストできます。

next buildを実行すると、Next.jsは静的エクスポートをoutフォルダに生成します。例えば、次のルートがある場合:

  • /
  • /blog/[id]

next buildを実行後、Next.jsは次のファイルを生成します:

  • /out/index.html
  • /out/404.html
  • /out/blog/post-1.html
  • /out/blog/post-2.html

Nginxのような静的ホストを使用している場合、受信リクエストから正しいファイルへのリライトを設定できます:

nginx.conf
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;
  }
}

バージョン履歴

バージョン変更内容
v14.0.0next exportが削除され、"output": "export"に置き換えられました
v13.4.0App Router(安定版)が追加され、Reactサーバーコンポーネントとルートハンドラを使用した静的エクスポートのサポートが強化されました
v13.3.0next exportが非推奨となり、"output": "export"に置き換えられました

On this page