Partial Prerendering
注意: Partial Prerenderingは実験的な機能であり、カナリアチャンネルでのみ利用可能で、変更される可能性があります。本番環境での使用にはまだ準備が整っていません。
Partial Prerendering (PPR)を使用すると、同じルート内で静的コンポーネントと動的コンポーネントを組み合わせることができます。
ビルド中に、Next.jsはルートの可能な限り多くをプレンダーします。dynamicなコードが検出された場合(例:受信リクエストの読み取り)、関連するコンポーネントをReact Suspenseの境界でラップできます。このSuspenseの境界のフォールバックはプレンダーされたHTMLに含まれます。
🎥 見る: なぜPPRなのか、そしてそれがどのように機能するのか → YouTube (10分)。
背景
PPRにより、Next.jsサーバーはすぐにプレンダーされたコンテンツを送信できます。
クライアントからサーバーへのウォーターフォールを防ぐために、動的コンポーネントは初期プレンダーを提供している間にサーバーから平行してストリーミングを開始します。これにより、動的コンポーネントがクライアントのJavaScriptがブラウザにロードされる前にレンダリングを開始できるようになります。
各動的コンポーネントのために多くのHTTPリクエストを作成しないようにするために、PPRは静的プレンダーと動的コンポーネントを1つのHTTPリクエストに結合できます。これにより、各動的コンポーネントに対して複数のネットワークラウンドトリップが必要なくなります。
Partial Prerenderingの使用
インクリメンタルアダプション (バージョン15)
Next.js 15では、ppr
オプションをnext.config.js
でincremental
に設定し、ファイルの上部でexperimental_ppr
のroute config optionをエクスポートすることで、layoutsやpagesでPartial Prerenderingを段階的に採用できます;
- TypeScript
- JavaScript
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
ppr: 'incremental',
},
}
export default nextConfig
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
ppr: 'incremental',
},
}
module.exports = nextConfig
- TypeScript
- JavaScript
import { Suspense } from "react"
import { StaticComponent, DynamicComponent, Fallback } from "@/app/ui"
export const experimental_ppr = true
export default function Page() {
return {
<>
<StaticComponent />
<Suspense fallback={<Fallback />}>
<DynamicComponent />
</Suspense>
</>
};
}
import { Suspense } from "react"
import { StaticComponent, DynamicComponent, Fallback } from "@/app/ui"
export const experimental_ppr = true
export default function Page() {
return {
<>
<StaticComponent />
<Suspense fallback={<Fallback />}>
<DynamicComponent />
</Suspense>
</>
};
}
Good to know:
experimental_ppr
を持たないルートはデフォルトでfalse
となり、PPRを使用してプレンダーされません。各ルートに対してPPRを使用することを明示的に選択する必要があります。experimental_ppr
は、ネストされたlayoutsやpagesを含むルートセグメントのすべての子に適用されます。すべてのファイルに追加する必要はなく、ルートの最上部のセグメントのみに追加すればよいです。- 子セグメントのPPRを無効にするには、子セグメントで
experimental_ppr
をfalse
に設定できます。
動的コンポーネント
next build
中にルートのプレンダーを作成する際、Next.jsはReact Suspenseで動的なAPIをラップする必要があります。そのfallback
はプレンダーに含まれます。
たとえば、cookies
やheaders
のような関数を使用する場合:
- JavaScript
- TypeScript
import { cookies } from 'next/headers'
export async function User() {
const session = (await cookies()).get('session')?.value
return '...'
}
import { cookies } from 'next/headers'
export async function User() {
const session = (await cookies()).get('session')?.value
return '...'
}
このコンポーネントは、cookieを読み取るために受信リクエストを参照する必要があります。これをPPRで使用するには、コンポーネントをSuspenseでラップする必要があります:
- TypeScript
- JavaScript
import { Suspense } from 'react'
import { User, AvatarSkeleton } from './user'
export const experimental_ppr = true
export default function Page() {
return (
<section>
<h1>これはプレンダーされます</h1>
<Suspense fallback={<AvatarSkeleton />}>
<User />
</Suspense>
</section>
)
}
import { Suspense } from 'react'
import { User, AvatarSkeleton } from './user'
export const experimental_ppr = true
export default function Page() {
return (
<section>
<h1>これはプレンダーされます</h1>
<Suspense fallback={<AvatarSkeleton />}>
<User />
</Suspense>
</section>
)
}
コンポーネントは値がアクセスされたときに初めて動的レンダリングを選択します。
たとえば、page
からsearchParams
を読み取る場合、この値を別のコンポーネントにprop
として渡すことができます:
- TypeScript
- JavaScript
import { Table } from './table'
export default function Page({
searchParams,
}: {
searchParams: Promise<{ sort: string }>
}) {
return (
<section>
<h1>これはプレンダーされます</h1>
<Table searchParams={searchParams} />
</section>
)
}
import { Table } from './table'
export default function Page({ searchParams }) {
return (
<section>
<h1>これはプレンダーされます</h1>
<Table searchParams={searchParams} />
</section>
)
}
テーブルコンポーネントの内で、searchParams
からの値にアクセスすると、コンポーネントは動的に実行されます:
- TypeScript
- JavaScript
export async function Table({
searchParams,
}: {
searchParams: Promise<{ sort: string }>
}) {
const sort = (await searchParams).sort === 'true'
return '...'
}
export async function Table({ searchParams }) {
const sort = (await searchParams).sort === 'true'
return '...'
}