Next.js におけるレンダリングを理解する


こんにちは、テクノロジー・コンサルティング課のゴマ太郎です。
今回は Next.js におけるレンダリングについて調査しましたのでここで共有できればと思います。

1. はじめに

今回は App Router について調査しております。
Next.js の App Router では、デフォルトで Server Components が採用されており、 Page Router とは異なるレンダリングの仕組みが導入されています。
このレンダリングの挙動を理解することは、パフォーマンスの最適化やバグの防止に重要になってきます。

2. 初回レンダリングの順序

Next.js の App Router における初回レンダリングは、以下の順序で実行されます:

  1. サーバーサイドでのレンダリング
    • ルートレイアウトから順に Server Components をレンダリング
    • 静的 HTML を生成
    • RSC ペイロードを生成し、ストリーム送信
  2. クライアントサイドでのハイドレーション
    • 静的 HTML を描画(すぐにユーザに見える)
    • RSC ペイロードを受信し、React ツリーを再構築
    • Client Components を対象にハイドレーション(既存 HTML と突き合わせ)
    • イベントリスナーが付与され、インタラクティブ化


「ストリーミング」の概念
App Router の大きな特徴の一つは、レンダリングが完了した部分から順次クライアントに送信するストリーミングです。これにより、体感的な表示速度が向上します。

Client Components の初期 HTML
サーバーサイドの段階で、Client Components も初期状態の HTML としてレンダリングされます。そのため、 JavaScript がまだ読み込まれていなくても、ボタンや入力フィールドの「見た目」は表示されます。その後、クライアントサイドでハイドレーションが行われ、「機能」が追加されるイメージです。

3. 再レンダリングの条件とタイミング

App Router では、再レンダリングの制御が以前より細かくなっています。
主な再レンダリングの条件は以下の通りです:

  1. Server Components の再レンダリング
    • ルート変更時
    • データの再検証(revalidation)時
    • サーバーアクション実行時
  2. Client Components の再レンダリング
    • 状態(state)の変更時
    • プロパティ(props)の変更時
    • 親コンポーネントの再レンダリング時
    • コンテキスト(context)の値変更時

    これら Client Components の再レンダリングのトリガーは、従来の React の挙動と同じです。重要なのは、 Client Components の再レンダリングはブラウザ上のみで完結し、サーバーへのリクエストは発生しないという点です。これにより、 UI のインタラクションに対して高速なフィードバックが可能になります。

4. Server Components と Client Components の連携

App Router の強力な点は、これら2種類のコンポーネントをシームレスに連携させられることです。

Server Components から Client Components へ

最も一般的なパターンは、Server Components でデータを取得し、それを Client Components に props として渡す方法です。これにより、データの取得はサーバーで行い、インタラクティブな部分はクライアントで描画するという役割分担ができます。

import InteractiveButton from './InteractiveButton'

async function getUserData() {
  // サーバーサイドでのみ実行されるデータ取得処理
  const res = await fetch('https://api.example.com/user/1', { cache: 'no-store' })
  return res.json()
}

export default async function Page() {
  const user = await getUserData()

  return (
    <main>
      <h1>User Profile</h1>
      <p>Name: {user.name}</p>
      <InteractiveButton initialCount={user.loginCount} />
    </main>
  )
}

'use client'

import { useState } from 'react'

export default function InteractiveButton({ initialCount }) {
  const [count, setCount] = useState(initialCount)
  return (
    <button onClick={() => setCount(count + 1)}>
      Login Count: {count}
    </button>
  )
}

Client Components 内で Server Components を利用する

Client Components に Server Components を直接インポートすることはできません。しかし、props として Server Components を渡すことで、擬似的にネスト(入れ子)にすることが可能です。`children` props はこの典型的な例です。

レイアウトコンポーネントが良い例で、Client Component で作成したタブ切り替えUIの各タブ内に、Server Component で描画された静的なコンテンツを表示する、といった複雑な構成も実現できます。

import TabContainer from './TabContainer'
import StaticContent from './StaticContent'
import DynamicContent from './DynamicContent'

export default function RootLayout() {
  return (
    <html>
      <body>
        {/* Client Component に Server Components を children として渡す */}
        <TabContainer tabs={{
          'Static': <StaticContent />,
          'Dynamic': <DynamicContent />
        }} />
      </body>
    </html>
  )
}

5. レンダリングの使い分け

では、どのような基準で Server Components と Client Components を使い分ければ良いのでしょうか。以下の表にまとめました。

Server Components Client Components
主な役割 データ取得, バックエンド処理, UIの骨格 インタラクティブなUI, ブラウザAPI利用
実行環境 サーバー クライアント (ブラウザ)
データアクセス DB直結, ファイルシステム, 外部API 不可(API経由のみ)
インタラクティブ性(useState, useEffect) 不可 可能
バンドルサイズ クライアントへの影響なし 影響あり
適した用途 静的ページ, データ表示, 認証チェック ボタンやフォーム, アニメーション, 状態管理

基本は「できる限り Server Components を使い、インタラクティブ性が必要な部分だけを Client Components に切り出す」ことです。
これにより、クライアントに送信する JavaScript の量を最小限に抑え、初期表示速度を向上させることができます。

6. まとめ

今回は Next.js の App Router におけるレンダリングの仕組みについて解説しました。

  • 初回表示は、サーバーでレンダリングされた HTML と RSC ペイロードを元に、クライアントがハイドレーションを行います。
  • 再レンダリングは、 Server Components と Client Components でトリガーが異なります。
  • コンポーネントは役割に応じて適切に使い分けることが、パフォーマンス最適化の鍵です。

App Router のレンダリングモデルは、サーバーのパワーとクライアントのインタラクティブ性を両立させるための非常に強力な仕組みです。
この挙動を正しく理解し、コンポーネントを適切に設計することで、ユーザー体験の優れた高速な Web アプリケーションを構築できるでしょう。

この記事を書いた人


ニックネーム
ゴマ太郎
経歴
新卒入社4年目(22卒)
昨年度から新設されたテクノロジー・コンサルティング課(TC課)にて、セールスエンジニアとして提案活動をしながら開発業務も行っております。
TC課の業務のほかにも、新卒採用でのリクルータ業務や新人研修でのメンター業務、時々マーケティングのお手伝いなどもしております。



\DX推進・AI導入支援なら、株式会社シー・エス・エスへ/