03. Suspense境界との統合

静的シェルと動的コンテンツを組み合わせて、高速で柔軟なページを構築しましょう。

静的シェル + 動的コンテンツ

ページの静的部分をキャッシュし、動的部分はSuspenseでストリーミング

基礎
基本的なパターン
import { Suspense } from "react";

// 静的シェル(キャッシュされる)
async function Header() {
  "use cache";
  return <header>Static Header</header>;
}

// 動的コンテンツ
async function DynamicContent() {
  const data = await fetchRealtimeData();
  return <div>{data}</div>;
}

export default async function Page() {
  return (
    <div>
      <Header /> {/* 即座に表示 */}
      <Suspense fallback={<Loading />}>
        <DynamicContent /> {/* ストリーミング */}
      </Suspense>
    </div>
  );
}

実際の動作デモ:

静的シェル(即座に表示)

HIT12:10:47

Next Lift Dashboard

あなたのトレーニング管理システム

キャッシュ時刻

動的コンテンツ(ストリーミング)

💡 ページをリロードすると、静的シェルは即座に表示され、動的コンテンツは1秒後にストリーミングで表示されます。

このパターンのメリット

  • 初回表示が高速: 静的部分はキャッシュから即座に表示
  • 段階的レンダリング: 準備できた部分から順次表示
  • 柔軟性: 静的と動的のバランスを自由に調整
  • SEO対策: 重要なコンテンツは静的にプリレンダリング

ネストしたSuspense境界

複数のSuspenseを組み合わせて、きめ細かい制御を実現

基礎
ネストしたSuspense
// ネストしたSuspense境界
export default function Page() {
  return (
    <div>
      <StaticHeader />

      <Suspense fallback={<HeaderSkeleton />}>
        <DynamicHeader />

        <div className="grid grid-cols-2">
          <Suspense fallback={<CardSkeleton />}>
            <StatsCard />
          </Suspense>

          <Suspense fallback={<CardSkeleton />}>
            <ActivityCard />
          </Suspense>
        </div>
      </Suspense>
    </div>
  );
}

使用ケース

  • ダッシュボード: 各ウィジェットを独立してストリーミング
  • 商品ページ: 商品情報、レビュー、関連商品を別々に表示
  • 記事ページ: 本文、コメント、推奨記事を段階的にロード

⚠️ 注意点

  • • Suspense境界を細かくしすぎると管理が複雑に
  • • スケルトンUIは実際のコンテンツサイズに近づける
  • • ネストが深すぎるとパフォーマンスに影響

ハイブリッドキャッシング

キャッシュされたコンポーネント内に動的コンテンツを配置

基礎
ハイブリッドパターン
// ハイブリッドパターン
async function OptimizedPage() {
  "use cache";
  cacheLife("hours");

  const staticData = await getStaticData();

  return (
    <Layout data={staticData}> {/* キャッシュされた部分 */}
      <Suspense fallback={<Skeleton />}>
        <DynamicWidget /> {/* 動的な部分 */}
      </Suspense>
    </Layout>
  );
}

ハイブリッドパターンの特徴

「use cache」でマークされたコンポーネント内に、Suspenseで囲まれた動的コンテンツを配置できます。 これにより、レイアウトやナビゲーションなどの静的部分をキャッシュしながら、 コンテンツの一部を動的に保つことができます。

✅ ベストプラクティス

  • • レイアウトやナビゲーションは「use cache」でキャッシュ
  • • ユーザー固有のデータはSuspenseで動的に取得
  • • スケルトンUIで優れたUXを提供
  • • cacheLife()で適切な再検証タイミングを設定

フェーズ1完了おめでとうございます!

基礎機能(優先度A)をマスターしました。次は実用的な機能(優先度B)に進みましょう。 cacheTag()による再検証、プライベートキャッシュ、connection() APIなど、実際のアプリケーション開発で役立つ機能を学びます。