ローディングUIとストリーミング

特別なファイル loading.js を使用すると、React Suspense を活用して意味のあるローディングUIを作成できます。この規約により、ルートセグメントのコンテンツがロードされている間にサーバーから即時ローディング状態を表示できます。レンダリングが完了すると、新しいコンテンツが自動的に切り替わります。

ローディングUI

即時ローディング状態

即時ローディング状態とは、ナビゲーション時に直ちに表示されるフォールバックUIです。スケルトンやスピナーなどのローディングインジケーター、または将来の画面の一部(カバー写真、タイトルなど)を事前にレンダリングできます。これにより、アプリが反応していることをユーザーが理解し、より良いユーザー体験を提供できます。

フォルダ内に loading.js ファイルを追加することでローディング状態を作成します。

loading.js 特別ファイル
export default function Loading() {
  // Loading内に任意のUIを追加可能(スケルトンを含む)
  return <LoadingSkeleton />
}
export default function Loading() {
  // Loading内に任意のUIを追加可能(スケルトンを含む)
  return <LoadingSkeleton />
}

同じフォルダ内では、loading.jslayout.js 内にネストされます。自動的に page.js ファイルとその子コンポーネントを <Suspense> バウンダリでラップします。

loading.js 概要

知っておくと便利:

  • サーバー中心のルーティングでもナビゲーションは即時に行われます。
  • ナビゲーションは中断可能で、ルートのコンテンツが完全にロードされる前に別のルートに移動できます。
  • 新しいルートセグメントがロードされている間も共有レイアウトは操作可能です。

推奨事項: Next.jsがこの機能を最適化するため、ルートセグメント(レイアウトとページ)には loading.js 規約を使用してください。

Suspenseによるストリーミング

loading.js に加えて、独自のUIコンポーネントに対して手動でSuspenseバウンダリを作成できます。App RouterはNode.jsとEdgeランタイムの両方でSuspenseによるストリーミングをサポートしています。

知っておくと便利:

  • 一部のブラウザはストリーミングレスポンスをバッファリングします。1024バイトを超えるまでストリーミングレスポンスが表示されない場合があります。これは通常「Hello World」アプリケーションにのみ影響し、実際のアプリケーションには影響しません。

ストリーミングとは?

ReactとNext.jsにおけるストリーミングの仕組みを理解するには、サーバーサイドレンダリング (SSR) とその制限について知るのが役立ちます。

SSRでは、ユーザーがページを表示して操作できるようになる前に完了する必要がある一連のステップがあります:

  1. まず、特定のページのすべてのデータがサーバーでフェッチされます。
  2. 次にサーバーがページのHTMLをレンダリングします。
  3. ページのHTML、CSS、JavaScriptがクライアントに送信されます。
  4. 生成されたHTMLとCSSを使用して非インタラクティブなUIが表示されます。
  5. 最後にReactがUIをハイドレートしてインタラクティブにします。
ストリーミングなしのサーバーレンダリング図

これらのステップは順次的でブロッキングされるため、サーバーはすべてのデータがフェッチされた後にのみページのHTMLをレンダリングできます。またクライアントでは、ページ内のすべてのコンポーネントのコードがダウンロードされるまでReactはUIをハイドレートできません。

ReactとNext.jsのSSRは、可能な限り早く非インタラクティブなページをユーザーに表示することで、知覚されるローディングパフォーマンスを向上させます。

ストリーミングなしのサーバーレンダリング

しかし、サーバー上のすべてのデータフェッチが完了するまでページをユーザーに表示できないため、依然として遅くなる可能性があります。

ストリーミングにより、ページのHTMLを小さなチャンクに分割し、それらのチャンクをサーバーからクライアントに段階的に送信できます。

ストリーミングを伴うサーバーレンダリングの仕組み

これにより、すべてのデータがロードされる前にUIの一部をより早く表示できます。

ストリーミングはReactのコンポーネントモデルとうまく連携します。各コンポーネントはチャンクと見なすことができるためです。優先度の高いコンポーネント(例: 製品情報)やデータに依存しないコンポーネント(例: レイアウト)を最初に送信でき、Reactはより早くハイドレートを開始できます。優先度の低いコンポーネント(例: レビュー、関連製品)は、データがフェッチされた後に同じサーバーリクエストで送信できます。

ストリーミングを伴うサーバーレンダリング図

ストリーミングは、長いデータリクエストがページレンダリングをブロックするのを防ぎたい場合に特に有益です。Time To First Byte (TTFB)First Contentful Paint (FCP) を減少させ、特に低速なデバイスでは Time to Interactive (TTI) を改善できます。

<Suspense> は非同期アクション(例: データフェッチ)を実行するコンポーネントをラップし、処理中にフォールバックUI(例: スケルトン、スピナー)を表示し、アクションが完了するとコンポーネントを入れ替えます。

import { Suspense } from 'react'
import { PostFeed, Weather } from './Components'

export default function Posts() {
  return (
    <section>
      <Suspense fallback={<p>フィードをロード中...</p>}>
        <PostFeed />
      </Suspense>
      <Suspense fallback={<p>天気情報をロード中...</p>}>
        <Weather />
      </Suspense>
    </section>
  )
}
import { Suspense } from 'react'
import { PostFeed, Weather } from './Components'

export default function Posts() {
  return (
    <section>
      <Suspense fallback={<p>フィードをロード中...</p>}>
        <PostFeed />
      </Suspense>
      <Suspense fallback={<p>天気情報をロード中...</p>}>
        <Weather />
      </Suspense>
    </section>
  )
}

Suspenseを使用することで以下の利点が得られます:

  1. ストリーミングサーバーレンダリング - サーバーからクライアントへHTMLを段階的にレンダリング
  2. 選択的ハイドレーション - Reactはユーザーの操作に基づいて、最初にインタラクティブにするコンポーネントを優先します

Suspenseのその他の例やユースケースについては、Reactドキュメントをご覧ください。

SEO

  • Next.jsはgenerateMetadata内のデータフェッチが完了するまで、UIのクライアントへのストリーミングを待機します。これにより、ストリーミングレスポンスの最初の部分に<head>タグが含まれることが保証されます。
  • ストリーミングはサーバーサイドレンダリングされるため、SEOに影響しません。Googleのリッチリザルトテストツールを使用して、ページがGoogleのウェブクローラーにどのように表示されるかを確認し、シリアライズされたHTMLを表示できます(出典)。

ステータスコード

ストリーミング時には、リクエストが成功したことを示すために200ステータスコードが返されます。

サーバーは、ストリーミングコンテンツ内でエラーや問題をクライアントに伝えることができます(例: redirectnotFound を使用時)。レスポンスヘッダーは既にクライアントに送信されているため、レスポンスのステータスコードを更新することはできません。これはSEOに影響しません。