Partial Prerendering
Note: Partial Prerenderingは実験的な機能で、canaryでのみ利用可能であり、変更される可能性があります。プロダクションでの使用にはまだ準備ができていません。
Partial Prerendering (PPR)は、同じルート内で静的コンポーネントと動的コンポーネントを組み合わせることを可能にします。
ビルド中に、Next.jsは可能な限りルートを事前レンダリングします。リクエストからの読み取りのような動的コードが検出された場合、関連するコンポーネントをReact Suspense境界でラップできます。Suspense境界のフォールバックは、事前レンダリングされたHTMLに含まれます。
🎥 視聴: PPRの理由とその仕組み → YouTube (10分)。
背景
PPRは、Next.jsサーバーが事前レンダリングされたコンテンツを即座に送信できるようにします。
クライアントからサーバーへのウォーターフォールを防ぐために、動的コンポーネントは初期の事前レンダリングを提供しながら、サーバーから並行してストリーミングを開始します。これにより、クライアントのJavaScriptがブラウザにロードされる前に動的コンポーネントがレンダリングを開始できるようになります。
各動的コンポーネントに対して多くのHTTPリクエストを作成することを防ぐために、PPRは静的な事前レンダリングと動的コンポーネントを単一のHTTPリクエストにまとめることができます。これにより、各動的コンポーネントに対して複数のネットワーク往復が必要になることはありません。
Partial Prerenderingの使用
インクリメンタルな採用(バージョン15 Canaryバージョン)
Next.js 15のcanaryバージョンでは、PPRは実験的な機能として利用可能です。まだ安定版では利用できません。インストールするには:
npm install next@canary
Partial Prerenderingをlayoutsやpagesでインクリメンタルに採用するには、next.config.js
でppr
オプションをincremental
に設定し、ファイルの先頭でexperimental_ppr
ルート設定オプションをエクスポートします:
- 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は動的APIがReact Suspenseでラップされていることを要求します。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 '...'
}
このコンポーネントは、クッキーを読み取るために受信リクエストを確認する必要があります。これを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 '...'
}