メインコンテンツまでスキップ

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をlayoutspagesでインクリメンタルに採用するには、next.config.jspprオプションをincrementalに設定し、ファイルの先頭でexperimental_ppr ルート設定オプションをエクスポートします:

next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
experimental: {
ppr: 'incremental',
},
}

export default nextConfig
app/page.tsx
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_pprfalseに設定できます。

動的コンポーネント

next build中にルートの事前レンダリングを作成する際、Next.jsは動的APIがReact Suspenseでラップされていることを要求します。fallbackは事前レンダリングに含まれます。

たとえば、cookiesheadersのような関数を使用する場合:

app/user.js
import { cookies } from 'next/headers'

export async function User() {
const session = (await cookies()).get('session')?.value
return '...'
}

このコンポーネントは、クッキーを読み取るために受信リクエストを確認する必要があります。これをPPRで使用するには、コンポーネントをSuspenseでラップする必要があります:

app/page.tsx
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として別のコンポーネントに転送できます:

app/page.tsx
import { Table } from './table'

export default function Page({
searchParams,
}: {
searchParams: Promise<{ sort: string }>
}) {
return (
<section>
<h1>これは事前レンダリングされます</h1>
<Table searchParams={searchParams} />
</section>
)
}

テーブルコンポーネント内で、searchParamsから値をアクセスすると、コンポーネントは動的に実行されます:

app/table.tsx
export async function Table({
searchParams,
}: {
searchParams: Promise<{ sort: string }>
}) {
const sort = (await searchParams).sort === 'true'
return '...'
}