Static Exports
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のコアは静的エクスポートをサポートするように設計されています。
Server Components
静的エクスポートを生成するためにnext build
を実行すると、app
ディレクトリ内で使用されるServer Componentsは、従来の静的サイト生成と同様にビルド中に実行されます。
結果として得られるコンポーネントは、初期ページロード用の静的HTMLと、ルート間のクライアントナビゲーション用の静的ペイロードとしてレンダリングされます。静的エクスポートを使用する際、Server Componentsを変更する必要はありません。ただし、dynamic server functionsを使用する場合を除きます。
- TypeScript
export default async function Page() {
// このフェッチは`next build`時にサーバーで実行されます
const res = await fetch('https://api.example.com/...')
const data = await res.json()
return <main>...</main>
}
Client Components
クライアントでデータフェッチを行いたい場合、Client ComponentをSWRと組み合わせてリクエストをメモ化することができます。
- TypeScript
- JavaScript
'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
}
'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
}
ルート遷移はクライアント側で行われるため、これは従来のSPAのように動作します。例えば、次のインデックスルートでは、クライアント上で異なる投稿にナビゲートできます:
- TypeScript
- JavaScript
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>
</>
)
}
import Link from 'next/link'
export default function Page() {
return (
<>
<h1>Index Page</h1>
<p>
<Link href="/other">Other Page</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を構築します:
- TypeScript
- JavaScript
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}`
}