Menu

Partial Prerendering

注意: Partial Prerendering は実験的な機能で、canary でのみ利用可能であり、変更される可能性があります。本番環境での使用には適していません。

Partial Prerendering (PPR) を使用すると、同じルート内で静的コンポーネントと動的コンポーネントを組み合わせることができます。

ビルド時に、Next.js はルートの可能な限り多くの部分を事前レンダリングします。動的コードが検出された場合(受信リクエストの読み取りなど)、関連するコンポーネントを React Suspense 境界でラップできます。Suspense 境界のフォールバックは、事前レンダリングされた HTML に含められます。

静的なナビゲーションと製品情報、動的なカートと推奨製品を示す部分的に事前レンダリングされた製品ページ

🎥 視聴: PPR とその仕組みについて → YouTube (10分)

背景

PPR により、Next.js サーバーは即座に事前レンダリングされたコンテンツを送信できます。

クライアントからサーバーへの連鎖を防ぐため、動的コンポーネントは初期事前レンダリングの提供と並行してサーバーからストリーミングを開始します。これにより、クライアント JavaScript がブラウザに読み込まれる前に動的コンポーネントのレンダリングを開始できます。

各動的コンポーネントに対して多数の HTTP リクエストを作成することを防ぐため、PPR は静的事前レンダリングと動的コンポーネントを単一の HTTP リクエストに組み合わせることができます。これにより、各動的コンポーネントに必要な複数のネットワークラウンドトリップを防ぎます。

Partial Prerendering の使用

段階的な導入(Version 15)

Next.js 15 では、next.config.jsppr オプションを incremental に設定し、ファイルの先頭で experimental_ppr ルート設定オプションをエクスポートすることで、レイアウトページで Partial Prerendering を段階的に採用できます:

next.config.ts
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  experimental: {
    ppr: 'incremental',
  },
}
 
export default nextConfig
next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    ppr: 'incremental',
  },
}
 
module.exports = nextConfig
app/page.tsx
TypeScript
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>
     </>
  };
}

補足:

  • experimental_ppr がないルートはデフォルトで false となり、PPR を使用して事前レンダリングされません。各ルートで明示的に PPR を有効にする必要があります。
  • experimental_ppr はルートセグメントのすべての子(ネストされたレイアウトとページを含む)に適用されます。すべてのファイルに追加する必要はなく、ルートのトップセグメントにのみ追加します。
  • 子セグメントで PPR を無効にするには、子セグメントで experimental_pprfalse に設定できます。

PPR の有効化(Version 14)

バージョン 14 では、next.config.js ファイルに ppr オプションを追加することで有効にできます。これはアプリケーション内のすべてのルートに適用されます:

next.config.ts
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  experimental: {
    ppr: true,
  },
}
 
export default nextConfig
next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    ppr: true,
  },
}
 
module.exports = nextConfig

動的コンポーネント

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

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

app/user.tsx
TypeScript
import { cookies } from 'next/headers'
 
export async function User() {
  const session = (await cookies()).get('session')?.value
  return '...'
}

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

app/page.tsx
TypeScript
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 を読み取る場合、この値を別のコンポーネントにプロップとして渡すことができます:

app/page.tsx
TypeScript
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
TypeScript
export async function Table({
  searchParams,
}: {
  searchParams: Promise<{ sort: string }>
}) {
  const sort = (await searchParams).sort === 'true'
  return '...'
}