PPR の紹介と実装【Next.js 16】
はじめに
先日、Next.js の勉強会で、PPR(Partial Prerendering)について取り上げました 🫐
静的なサイトの高速性を保ちながら、動的なコンテンツとの組み合わせを可能にする技術は、
モダンな Web アプリケーションにおいて非常に強力です。
今回は、Next.js 16 の PPR について調査したので、基礎的な内容をまとめました!
時間の節約になれば、嬉しいです 🙌
PPR とは?
PPR(Partial Prerendering)とは、
ページの一部を事前にレンダリングし、他の部分を動的に処理することで、パフォーマンスと柔軟性を両立させる機能です
従来のレンダリング手法では、ページ全体を静的にするか動的にするかの二択でしたが、
PPR では、同一ページ内で静的な部分と動的な部分を組み合わせることができます!
今までの Next.js のレンダリング手法のおさらい
⚫️ SSG(Static Site Generation):
- ページ全体を静的にレンダリングする
-
npm run buildでビルドするタイミングで、データフェッチなどを行い、HTML を生成 - これにより、リクエストが来たときは、すでに HTML が生成されているため、高速に表示できる
- しかし、データが古くなる可能性がある
👇 こういうこと
npm run build
ビルド結果を確認すると、以下のような出力が表示されます:
Route (app)
┌ ○ /
├ ○ /_not-found
└ ● /projects/[id]
├ /projects/1
├ /projects/2
└ /projects/3
○ (Static) prerendered as static content
● (SSG) prerendered as static HTML (uses generateStaticParams)
⚫️ SSR(Server-Side Rendering):
- リクエストごとにレンダリングする
- リクエストが来た時に、データフェッチなどを行い、HTML を生成
- これにより、リクエストが来たときに、最新のデータを取得できる
- しかし、毎回サーバー処理が必要で、SSG と比べると遅いです
👇 こういうこと
npm run build
ビルド結果を確認すると、以下のような出力が表示されます:
Route (app)
┌ ○ /
├ ○ /_not-found
└ ƒ /projects/[id]
○ (Static) prerendered as static content
ƒ (Dynamic) server-rendered on demand
重要なことは、
- 今までは、ページ全体を静的にするか動的にするかの二択でした。
- PPR では、ページの大部分を静的に生成しながら、一部を動的にするという、ハイブリッドな手法が可能です!
PPR の利点
PPR の主な利点として、以下が挙げられます:
- 初期ロード時間の短縮: 静的な部分を事前にレンダリングすることで、ページの表示が高速化
- パーソナライゼーション: 動的な部分でユーザー固有のコンテンツを提供
- SEO 最適化: 静的な部分は検索エンジンに適切にインデックスされる
静的レンダリングの高速性と動的レンダリングの柔軟性を、同時に活用できるので、便利です 👍
👇
npm run build
ビルド結果を確認すると:
Route (app) Revalidate Expire
┌ ○ / 15m 1y
├ ○ /_not-found
└ ◐ /projects/[id] 15m 1y
├ /projects/[id] 15m 1y
├ /projects/1 15m 1y
├ /projects/2 15m 1y
└ /projects/3 15m 1y
○ (Static) prerendered as static content
◐ (Partial Prerender) prerendered as static HTML with dynamic server-streamed content
PPR の使い方
Next.js 16 では、PPR が Cache Components として統合され、
"use cache" ディレクティブを使用してコンポーネントレベルでキャッシュを制御できるようになりました。
実装方法について、詳しく見ていきましょう!
1: Cache Components の有効化
まず、next.config.js ファイルで Cache Components を有効にする必要があります。
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
cacheComponents: true,
};
export default nextConfig;
この設定により、PPR の機能が利用可能になります!
2: "use cache" ディレクティブの使用
キャッシュしたいコンポーネントの先頭に "use cache"; ディレクティブを追加します。
// components/ProductInfo.tsx
"use cache";
interface Product {
id: string;
name: string;
description: string;
price: number;
}
const getProduct = async (id: string): Promise<Product> => {
const response = await fetch(`https://api.example.com/products/${id}`);
return response.json();
};
export default async function ProductInfo({
productId,
}: {
productId: string;
}) {
const product = await getProduct(productId);
return (
<div>
<h2>{product.name}</h2>
<p>{product.description}</p>
<p>価格: ¥{product.price.toLocaleString()}</p>
</div>
);
}
3: 静的部分と動的部分の組み合わせ
PPR の真価は、静的な部分と動的な部分を組み合わせた時に発揮されます:
// app/products/[id]/page.tsx
import ProductInfo from "@/components/ProductInfo";
import UserRecommendations from "@/components/UserRecommendations";
import { Suspense } from "react";
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
return (
<main>
{/* 静的にキャッシュされる部分 */}
<ProductInfo productId={id} />
{/* 動的に生成される部分 */}
<Suspense fallback={<div>Loading...</div>}>
<UserRecommendations userId="current-user" />
</Suspense>
</main>
);
}
この例では、商品情報は静的にキャッシュされ、ユーザーごとのおすすめ情報は動的に生成されます!
なぜ PPR なのか?
個人的に、Next.js 16 での、PPR と Cache Components の追加は、とても嬉しい変更です!
理由は主に2つ:
1. ハイブリッドなレンダリング手法が簡単になった

上記の、「クラウドファンディングアプリ」の画像にある通り、
1つのページ内で、 あまり変更されない部分と、動的なデータを表示させたい。。 というケースは珍しくないです。
- クラウドファンディングを行うプロジェクトの概要文は、あまり変更されない
- 現在の支援状況は、できるだけリアルタイムで正確に表示したい
そのようなケースで、今までは、ページ全体を SSG にするか、SSR にするかの二択でした。
SSG を行って、クライアントサイドで動的なデータを取得する、という方法も可能ですが、
それだと、「どういう理由でサーバー/クライアントサイドで処理を実行するのかの区別」がわかりにくくなってきます!
プロジェクトが複雑になるほど、上記が曖昧になり、判断の負荷が高くなりますね。
PPR は、これを簡単な記述で解決できます 👍
2. キャッシュの制御がより柔軟/明確になった
Next.js v15 までは、以下のような方法で、キャッシュが制御できました:
- fetch 関数でキャッシュのオプションを指定する
- ページレベルで revalidate や dynamic を指定する
- Dynamic APIを使用する
個人的には、
このような複数の記述方法、そしてキャッシュの動作を把握するのが、少し大変だと感じることもありました。
(自分と同じように、Next.js のキャッシュ周りの設定に関して、少し複雑さを感じていた開発者も多いかと思います 😎)
Next.js 16 では、ページ/コンポーネント/関数のあらゆるレベルで、"use cache" ディレクティブを使用して、キャッシュを制御できます!
例:
const getProduct = async (id: string): Promise<Product> => {
"use cache";
cacheLife("hours"); // 1時間キャッシュ
cacheTag("products"); // products タグでキャッシュを管理
const response = await fetch(`https://api.example.com/products/${id}`);
return response.json();
};
基本的に同じ"use cache"の記述で、設定できる点がシンプルです!
何より、制御したい処理の近くに、それぞれの設定を記述できるので、
キャッシュ設定の管理が、少し見やすくなった印象でもあります!
おわりに
最後まで読んでいただき、ありがとうございます 🥳
下記の、React コミュニティ(勉強会/共同開発)での、振り返りのような記事ですが、
少しでも参考になれば、嬉しいです!
そして、もし、間違いや補足情報などがありましたら、
ぜひコメントを追加してください!
Happy Hacking :)
参考
Discussion