Menu

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

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

ローディングUI

インスタントローディング状態

インスタントローディング状態は、ナビゲーション時にすぐに表示されるフォールバックUIです。スケルトンやスピナーなどのローディングインジケーター、または今後の画面の小さくても意味のある部分(カバーフォト、タイトルなど)を事前にレンダリングできます。これにより、アプリが応答していることをユーザーに理解してもらい、より良いユーザー体験を提供できます。

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

loading.js特殊ファイル
app/dashboard/loading.tsx
TypeScript
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を使用して、非対話的なユーザーインターフェースが表示されます。
  5. 最後に、Reactがハイドレーションを行い、ユーザーインターフェースを対話可能にします。
ストリーミングなしのサーバーレンダリングチャート

これらの手順は順次的でブロッキングであり、サーバーはすべてのデータがフェッチされるまでページのHTMLをレンダリングできません。また、クライアント側では、ページ内のすべてのコンポーネントのコードがダウンロードされるまで、Reactはユーザーインターフェースをハイドレーションできません。

ReactとNext.jsによるSSRは、できるだけ早く非対話的なページをユーザーに表示することで、知覚的なロード性能を改善するのに役立ちます。

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

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

ストリーミングを使用すると、ページのHTMLをより小さな断片に分解し、それらの断片をサーバーからクライアントに徐々に送信できます。

ストリーミングを使用したサーバーレンダリングの仕組み

これにより、すべてのデータのロードを待たずに、ページの一部をより早く表示できます。

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

ストリーミングを使用したサーバーレンダリングチャート

ストリーミングは、長いデータリクエストによるページレンダリングのブロックを防ぎ、最初のバイト到達時間(TTFB)初回コンテンツフル描画(FCP)を短縮できるため、特に有効です。また、特に低速なデバイスではインタラクティブになるまでの時間(TTI)の改善にも役立ちます。

<Suspense> は、非同期アクション(データのフェッチなど)を実行するコンポーネントをラップし、その間はフォールバックUI(スケルトンやスピナーなど)を表示し、アクションが完了したら対象のコンポーネントに置き換えることで機能します。

app/dashboard/page.tsx
TypeScript
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のRich Results Testツールを使用して、ページがGoogleのウェブクローラーにどのように表示されるかを確認し、シリアライズされたHTMLを表示できます(出典)。

ステータスコード

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

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