ローディング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を使用したストリーミングをサポートしています。

ストリーミングとは?

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

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

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

これらのステップは順次的でブロッキングされるため、サーバーはすべてのデータがフェッチされた後にのみページの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は、UIをクライアントにストリーミングする前にgenerateMetadata内のデータフェッチが完了するのを待機します。これにより、ストリーミングされたレスポンスの最初の部分に<head>タグが含まれることが保証されます。
  • ストリーミングはサーバーでレンダリングされるため、SEOに影響を与えません。GoogleのMobile Friendly Testツールを使用して、ページがGoogleのウェブクローラーにどのように表示されるかを確認し、シリアライズされたHTMLを表示できます(出典)。

ステータスコード

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

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