Lang x Lang

Static Exports

Next.js は、静的サイトまたはシングルページアプリケーション(SPA)として開始し、その後オプションで、require した server の機能を使用するためのアップグレードを可能にします。

next buildを実行すると、Next.js は route ごとに HTML ファイルを生成します。厳密な SPA を個々の HTML ファイルに分けることで、Next.js は Client 側で不必要な JavaScript code の loading を回避し、バンドル size を削減してページの読み込みを高速化することができます。

Next.js がこの静的な export をサポートしているため、HTML/CSS/JS の静的アセットを提供できる任意のウェブ server にデプロイおよびホスティングできます。

Configuration

next.config.jsの中で出力モードを変更し、静的な export を有効にします:

next.config.js
/**
 * @type {import('next').NextConfig}
 */
const nextConfig = {
  output: 'export',

  // Optional: Change links `/me` -> `/me/` and emit `/me.html` -> `/me/index.html`
  // trailingSlash: true,

  // Optional: Prevent automatic `/me` -> `/me/`, instead preserve `href`
  // skipTrailingSlashRedirect: true,

  // Optional: Change the output directory `out` -> `dist`
  // distDir: 'dist',
}

module.exports = nextConfig

next buildを実行した後、Next.js はあなたのアプリケーションのための HTML/CSS/JS の資産を含むoutフォルダーを生成します。

Supported Features

Next.js の中心部は、静的 export をサポートするように設計されています。

Server Components

next buildを実行して静的な export を生成するとき、appディレクトリ内で使用される Server Components は、従来の静的サイト生成と同様に、build の間に実行されます。

結果として得られる component は、初回のページ読み込みのために静的な HTML にレンダリングされ、また、client の routes 間のナビゲーション用の静的ペイロードとして機能します。静的な export を使用する際には、Server Components を変更する必要はありません。ただし、dynamic serverfunctions を使用する場合は例外です。

app/page.tsx
export default async function Page() {
  // This fetch will run on the server during `next build`
  const res = await fetch('https://api.example.com/...')
  const data = await res.json()

  return <main>...</main>
}

Client Components

client でデータの取り出しを行いたい場合は、SWR を用いた Client Component を使って Request をメモ化することができます。

app/other/page.tsx
'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
}
app/other/page.js
'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 'Failed to load'
  if (!data) return 'Loading...'

  return data.title
}

route の遷移はクライアント側で行われるため、これは従来の SPA のように動作します。例えば、以下の index route を使用すると、 client 上の異なる投稿に移動できます。

app/page.tsx
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>
    </>
  )
}
app/page.js
import Link from 'next/link'

export default function Page() {
  return (
    <>
      <h1>Index Page</h1>
      <p>
        <Link href="/other">Other Page</Link>
      </p>
    </>
  )
}

Image Optimization

Image Optimization は、next.config.jsでカスタムの image loader を定義することにより、静的な export と共にnext/imageを用いて使用できます。例えば、Cloudinary のようなサービスで画像を最適化することができます:

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export',
  images: {
    loader: 'custom',
    loaderFile: './my-loader.ts',
  },
}

module.exports = nextConfig

このカスタムの loader は、リモートの source から画像を fetch する方法を定義します。たとえば、以下の loader は Cloudinary の URL を構築します。

my-loader.ts
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}`
}
my-loader.js
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 の image への paths を相対的に定義することができます:

app/page.tsx
import Image from 'next/image'

export default function Page() {
  return <Image alt="turtles" src="/turtles.jpg" width={300} height={300} />
}
app/page.js
import Image from 'next/image'

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

Route ハンドラー

Route ハンドラーは、next buildを実行するときに静的な response を render します。サポートされるのはGET HTTPMethod のみです。これは、キャッシュされたデータまたはキャッシュされていないデータから静的な HTML、JSON、TXT、または他のファイルを生成するために使用できます。例えば:

app/data.json/route.ts
export async function GET() {
  return Response.json({ name: 'Lee' })
}
app/data.json/route.js
export async function GET() {
  return Response.json({ name: 'Lee' })
}

上記のファイル app/data.json/route.tsnext build中に、静的ファイルに render され、{ name: 'Lee' } を含む data.json を生成します。

あなたが入ってくる request から dynamic な値を読み取る必要がある場合、静的な export を使用することはできません。

ブラウザ API

Client Components はnext buildの間に HTML へレンダリング済みです。Web APIs 、例えばwindowlocalStorage、そしてnavigatorのような API が server 上では利用できないので、これらの API を安全に利用するためにはブラウザで実行する必要があります。例えば:

'use client';

import { useEffect } from 'react';

export default function ClientComponent() {
  useEffect(() => {
    // You now have access to `window`
    console.log(window.innerHeight);
  }, [])

  return ...;
}

Unsupported Features

Node.js server が必要な require 機能や、build プロセス中に計算できない dynamic ロジックはサポートされていません

これらの機能のいずれかを next dev で使用しようとすると、dynamic オプションを root layout の error に設定するのと同様の error が発生します。

export const dynamic = "error";

Deploying

静的な export を使って、HTML/CSS/JS の静的アセットをサーブできるあらゆるウェブ server で Next.js をデプロイおよびホストできます。

next buildを実行すると、 Next.js は静的な export をoutフォルダに生成します。例えば、次のような routes があるとします。

  • /
  • /blog/[id]

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

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

静的な host、例えば Nginx を使用している場合、着信 Request を適切なファイルに rewrites するよう設定できます:

nginx.conf
server {
  listen 80;
  server_name acme.com;

  root /var/www/out;

  location / {
      try_files $uri $uri.html $uri/ =404;
  }

  # This is necessary when `trailingSlash: false`.
  # You can omit this when `trailingSlash: true`.
  location /blog/ {
      rewrite ^/blog/(.*)$ /blog/$1.html break;
  }

  error_page 404 /404.html;
  location = /404.html {
      internal;
  }
}

Version History

VersionChanges
v14.0.0next export"output": "export"に優先して削除されました
v13.4.0App Router (安定版)は、強化された静的 export のサポートを追加し、React Server Components および Route ハンドラの使用も含まれています。
v13.3.0next exportは廃止され、"output": "export"に置き換えられました

当社サイトでは、Cookie を使用しています。各規約をご確認の上ご利用ください:
Cookie Policy, Privacy Policy および Terms of Use