はじめに
本記事では、Next.js でのレンダリング手法(CSR/SSR/SSG/ISR)の違いや適用方法と、RSC(React Server Components)、RCC(React Client Components)について解説します。
※こちらの記事の続編になります
想定読者
- React の基礎知識がある方
- RSC や RCC を活用したパフォーマンス向上に興味がある方
- Next.js のレンダリング戦略を学びたい方
本記事の目標
- RSCとRCCの使い方を学ぶ
- CSR、SSR、SSG、ISRの違いを理解する
1. RSC と RCC の違い
Next.js 15 では、コンポーネントが「サーバーコンポーネント(RSC)」と「クライアントコンポーネント(RCC)」の 2 種類に分かれています。
サーバーコンポーネント(RSC)はサーバー側(例:Vercel 上や Node.js 環境)でレンダリングされるため、データ取得やパフォーマンスに優れています。
一方、クライアントコンポーネント(RCC)はブラウザ(例:Chrome や Safari など)で動作し、ユーザーの操作に応じた動的な処理に適しています。
RSC (React Server Components)
- サーバー側で動作するコンポーネント
- Next.js のコンポーネントは、基本的にデフォルトで RSC になる
- RSC では React Hooks(例:
useState
やuseEffect
)を使用するとエラーになる
RCC (React Client Components)
- クライアント側(ブラウザ)で動作するコンポーネント
- React Hooks を使いたい場合は、RCC として作成する
- Next.js で RCC を定義するには、ファイル先頭に
'use client'
と書く必要がある
"use client";
import { useState } from "react";
const page = () => {
const [count, setCount] = useState(0);
console.log("client");
return (
<>
<div>クライアントコンポーネント</div>
<p>
<button
className="hover:cursor-pointer whitespace-pre"
onClick={() => setCount((prev) => ++prev)}
>
count:{" "}
</button>
{count}
</p>
</>
);
};
export default page;
2. RCC と RSC の組み合わせ
RCC と RSC は組み合わせて使用できます。
例えば、画面描画時に RSC でデータフェッチを行い、必要なデータを props で RCC に渡し、ユーザーアクション(クリックなど)の機能は RCC で実装する、といった役割分担が可能です。
なお、RCC 配下のコンポーネントは暗黙的に RCC として扱われます。
ex: RCC と RSC を組み合わせた画面構成
src/
├── app/
│ │── api/
│ │ └── menu/
│ │ └── route.ts
│ └── rsc/
│ └── page.tsx
└── components/
└── Contents.tsx
import { NextResponse } from "next/server";
export const GET = () => {
return NextResponse.json(["blog", "counter", "about", "contact"]);
};
import Contents from "@/components/Contents";
const page = async () => {
const response = await fetch("http://localhost:3000/api/menu", {
cache: "no-store",
});
const menuList: string[] = await response.json();
return (
<>
<div className="bg-green-400">サーバーコンポーネント</div>
<div className="flex flex-col md:flex-row">
{/* サイドメニュー */}
<ul className="w-full md:w-3/12 bg-green-200">
{menuList.map((menu, index) => (
<li
key={index}
className="hover:bg-green-300 cursor-pointer px-2 py-1 rounded"
>
{menu}
</li>
))}
</ul>
{/* メインコンテンツ(RCC) */}
<div className="w-full md:w-9/12">
<Contents initCount={menuList.length} />
</div>
</div>
</>
);
};
export default page;
"use client";
import { useEffect, useState } from "react";
const Contents = ({ initCount }: { initCount: number }) => {
const [count, setCount] = useState(0);
useEffect(() => setCount(initCount), [initCount]);
return (
<div className="bg-orange-200">
<p>クライアントコンポーネント</p>
<br />
<p className="pl-5">
<button
className="hover:cursor-pointer whitespace-pre"
onClick={() => setCount((prev) => ++prev)}
>
Counter:{" "}
</button>
{count}
</p>
</div>
);
};
export default Contents;
3. レンダリング
Next.js の App Router では、ページごとに CSR(Client-Side Rendering)、SSR(Server-Side Rendering)、SSG(Static Site Generation)、ISR(Incremental Static Regeneration)を柔軟に使い分けることができます。
ページ要件に応じて、これらのレンダリング手法を選択することで、パフォーマンスや UX を最適化できます。
注意
開発モード(npm run dev)では、すべてのページが SSR として動作します。
SSG や ISR の挙動を確認するには、本番ビルド(npm run build)と本番起動(npm start)が必要です。
CSR(Client-Side Rendering)
- クライアント側で HTML を生成する
- 動的にページを更新する
- ユーザーインタラクションが多いページや、SEO が不要なページに適している
SSR(Server-Side Rendering)
- サーバ側で HTML を生成する
- 常に最新のデータを取得する
- ユーザーごとに異なるコンテンツを表示するページや、SEO が重要なページに適している
ex: SSR で Dog API から犬の画像を取得
外部ドメインの画像を使用する場合は、next.config.ts で許可設定が必要です。
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
images: {
// 許可する外部ドメインのパターンを記載
remotePatterns: [
// Dog APIのURL
{
protocol: "https",
hostname: "images.dog.ceo",
},
],
},
};
export default nextConfig;
import Image from "next/image";
export const dynamic = "force-dynamic"; // 明示的にSSRを指定
const SSRPage = async () => {
const res = await fetch("https://dog.ceo/api/breeds/image/random", {
cache: "no-store",
});
if (!res.ok) {
throw new Error("Failed to fetch Dog data");
}
/** 犬の画像データ */
const image: string = (await res.json()).message;
/** タイムスタンプ */
const timestamp = new Date().toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' });
return (
<div>
<p>SSRで犬の画像を取得</p>
<p>Timestamp: {timestamp}</p>
<br />
<Image src={image} alt="dog" width={500} height={500} />
</div>
);
};
export default SSRPage;
このページでリロードすると、タイムスタンプが更新され、新しい犬の画像が表示できます。
SSG(Static Site Generation)
- ビルド時に静的 HTML を生成する
- 変更頻度が低いページや、初期表示速度を重視するページに適している
ex: SSG で PokéAPI からピカチュウのデータ取得
import Image from "next/image";
export const dynamic = "force-static"; // 明示的にSSGを指定
type Pokemon = {
name: string;
sprites: {
front_default: string;
};
types: {
type: {
name: string;
};
}[];
};
const SSGPage = async () => {
const res = await fetch("https://pokeapi.co/api/v2/pokemon/pikachu");
if (!res.ok) {
throw new Error("Failed to fetch Pikachu data");
}
/** ポケモンデータ */
const pokemon: Pokemon = await res.json();
/** タイムスタンプ */
const timestamp = new Date().toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' });
return (
<div>
<p>SSGでピカチュウのデータ取得</p>
<p>Timestamp: {timestamp}</p>
<br />
<p>名前:{pokemon.name}</p>
<p>タイプ:</p>
<ol>
{pokemon.types.map((typeInfo) => (
<li key={typeInfo.type.name}>{typeInfo.type.name}</li>
))}
</ol>
<Image
src={pokemon.sprites.front_default}
alt={pokemon.name}
width={300}
/>
</div>
);
};
export default SSGPage;
ピカチュウのデータが取得できました。
このページで何度ブラウザを更新しても、タイムゾーンが変わらないことが確認できます。
ISR(Incremental Static Regeneration)
- 一定時間ごとに静的ページを再生成する
- 最新ではないが適度に更新したいページや、大量のコンテンツを扱う場合に適している
ex: ISR で 10 秒ごとに国際宇宙ステーション(ISS)の現在位置を取得
export type IssNowResponse = {
timestamp: number;
iss_position: {
latitude: string;
longitude: string;
};
};
export const revalidate = 10; // 再生成の間隔を指定
const ISRPage = async () => {
const res = await fetch("http://api.open-notify.org/iss-now.json", {
next: { revalidate: 10 }, // 10秒ごとに再生成
});
if (!res.ok) {
throw new Error("Failed to fetch ISS data");
}
const data: IssNowResponse = await res.json();
return (
<div style={{ textAlign: "center", margin: "2rem" }}>
<h1>国際宇宙ステーションの現在位置</h1>
<p>緯度: {data.iss_position.latitude}</p>
<p>経度: {data.iss_position.longitude}</p>
<p>最終更新日時: {new Date(data.timestamp * 1000).toISOString()}</p>
</div>
);
};
export default ISRPage;
10 秒後にブラウザを更新すると、ISS の緯度・経度と最終更新日時が更新されることが確認できます。
まとめ
本記事では、Next.js のレンダリング戦略とコンポーネントについて学びました。
〈コンポーネント〉
- RSC (React Server Components): サーバー側で動作するコンポーネント。React Hooks は使えません。
-
RCC (React Client Components): クライアント側で動作するコンポーネント。
'use client'
を記述する必要があります。
〈レンダリング〉
- CSR (Client-Side Rendering): クライアント側で HTML を生成。動的なページ更新に適しています。
- SSR (Server-Side Rendering): サーバー側で HTML を生成。最新データや SEO が重要なページに適しています。
- SSG (Static Site Generation): ビルド時に静的 HTML を生成。初期表示速度が重視されるページに適しています。
- ISR (Incremental Static Regeneration): 定期的に静的ページを再生成。大量コンテンツや適度な更新が求められるページに適しています。
これらの戦略を状況に応じて使い分けることで、パフォーマンスや SEO を最適化できます。