Skip to content

Instantly share code, notes, and snippets.

@kobitoDevelopment
Created March 4, 2026 01:44
Show Gist options
  • Select an option

  • Save kobitoDevelopment/ee74fa5a59a8a77a6a9a6e8cd79c0a64 to your computer and use it in GitHub Desktop.

Select an option

Save kobitoDevelopment/ee74fa5a59a8a77a6a9a6e8cd79c0a64 to your computer and use it in GitHub Desktop.

Next.js App Router: Suspense と Server Components

Suspense で Server Component を wrap する目的

Server Component が非同期処理(データフェッチ等)を含む場合、その完了までページ全体のレンダリングがブロックされる。Suspense 境界で囲むことで、該当コンポーネントの処理完了を待つ間 fallback を表示しつつ、ページの残りを先行してクライアントへストリーミングできる。

import { Suspense } from 'react';

export default function Page() {
  return (
    <div>
      <h1>Dashboard</h1> {/* 即時レンダリング */}
      <Suspense fallback={<Skeleton />}>
        <SlowDataComponent /> {/* データ取得完了後にレンダリング */}
      </Suspense>
    </div>
  );
}

async function SlowDataComponent() {
  const data = await fetch('https://api.example.com/heavy-data');
  const json = await data.json();
  return <div>{json.title}</div>;
}

参考: Next.js - Streaming

使用すべきシチュエーション

1. 重いデータフェッチを含むコンポーネントの分離

ヘッダーやサイドバーなど即時表示可能な部分と、集計処理等で数秒かかる部分がある場合、後者だけを Suspense で囲むことで遅延箇所を局所化できる。

2. 複数の独立したデータソースの並列ストリーミング

<Suspense fallback={<Skeleton />}>
  <UserProfile />      {/* API A から取得 */}
</Suspense>
<Suspense fallback={<Skeleton />}>
  <Recommendations />  {/* API B から取得(低速) */}
</Suspense>

各 Suspense 境界が独立しているため、先に完了したコンポーネントから順次表示される。

3. loading.tsx より細かい粒度での制御

loading.tsx はルートセグメント全体に対する Suspense 境界として機能する。ページ内の特定の部分にのみローディング状態を適用したい場合は、手動で <Suspense> を配置する。

参考: Next.js - loading.tsx

"use client" との関係

Suspense 境界の配置

<Suspense> 自体は Server Component・Client Component どちらにも配置可能。React の機能であり、コンポーネントの種別に依存しない。

Suspense が待つ対象の違い

コンポーネント種別 非同期処理の方法 Suspense との連携
Server Component("use client" なし) async/await で直接データ取得可能 await の完了を Suspense が自動的に検知
Client Component("use client" あり) async コンポーネントにできない React.use() でPromise をunwrap、またはSWR / TanStack Query 等のライブラリ経由

Client Component での例

"use client";
import { use } from 'react';

function UserProfile({ dataPromise }: { dataPromise: Promise<User> }) {
  const user = use(dataPromise); // Promise を unwrap → Suspense が捕捉
  return <div>{user.name}</div>;
}

参考: React - use

実務上の傾向

Next.js App Router においては Server Component + Suspense の組み合わせが主流。Server Component では async/await するだけで Suspense が機能するため、明示的な連携コードが不要。Client Component 側で Suspense を使うのは、クライアント側で動的にデータを再取得するインタラクティブな UI(リアルタイム検索結果の更新等)に限定される。

SSG(静的生成)との関係

SSG ではビルド時にすべての非同期処理が完了し、完成した HTML が生成される。そのため Suspense の fallback がユーザーに表示されることはなく、ストリーミングによる段階的レンダリングの恩恵は得られない。

レンダリング方式 Suspense の効果
SSR(動的レンダリング) リクエスト時にストリーミングが発生し、Suspense 境界ごとに段階的に HTML が送信される
SSG(静的生成) ビルド時にすべて解決済み。Suspense を記述しても動作上のエラーにはならないが、UX 上の効果はない

参考: Next.js - Static and Dynamic Rendering

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment