Loading UIとストリーミング
特別なファイルloading.js
は、React Suspenseを使用して意味のあるLoading UIを作成するのに役立ちます。この規約を使用することで、ルートセグメントのコンテンツがロードされる間、サーバーから即時の読み込み状態を表示できます。レンダリングが完了すると、新しいコンテンツが自動的に入れ替えられます。
即時の読み込み状態
即時の読み込み状態は、ナビゲーション時に即座に表示されるフォールバックUIです。スケルトンやスピナーなどのロードインジケーター、またはカバー写真やタイトルなど将来の画面の小さく有意義な部分を事前にレンダリングすることができます。この方法は、ユーザーにアプリが反応していることを理解させ、ユーザー体験を向上させます。
loading.js
ファイルをフォルダー内に追加して、読み込み状態を作成してください。
- TypeScript
- JavaScript
export default function Loading() {
// スケルトンを含む、Loading内に任意のUIを追加できます。
return <LoadingSkeleton />
}
export default function Loading() {
// スケルトンを含む、Loading内に任意のUIを追加できます。
return <LoadingSkeleton />
}
同じフォルダー内で、loading.js
はlayout.js
内にネストされます。それは自動的にpage.js
ファイルとその下の子を<Suspense>
境界でラップします。
Good to know:
- サーバー中心のルーティングを使用してもナビゲーションは即座に行われます。
- ナビゲーションは中断可能であり、ルートを変更してもルートのコンテンツが完全にロードされるのを待つ必要はありません。
- 新しいルートセグメントがロードされても、共有レイアウトは対話可能なままです。
Recommendation: Next.jsはこの機能を最適化しているため、ルートセグメント(レイアウトとページ)に
loading.js
規約を使用してください。
Suspenseによるストリーミング
loading.js
に加えて、独自のUIコンポーネントのためのSuspense境界を手動で作成することもできます。App Router は、Node.jsおよびEdgeランタイム向けにSuspenseを使用したストリーミングをサポートしています。
Good to know:
- 一部のブラウザーはストリーミング応答をバッファします。応答が1024バイトを超えない限り、ストリーミングされた応答が表示されない場合があります。これは一般に "hello world" アプリケーションには影響しますが、実際のアプリケーションには影響しません。
ストリーミングとは何ですか?
ReactとNext.jsでストリーミングがどのように機能するかを理解するには、 サーバーサイドレンダリング(SSR) とその制約を理解することが役立ちます。
SSRでは、ユーザーがページを見て操作できるようになる前に完了しなければならない一連のステップがあります:
- 最初に、特定のページのすべてのデータがサーバーで取得されます。
- 次に、サーバーがページのHTMLをレンダリングします。
- ページのHTML、CSS、およびJavaScriptがクライアントに送信されます。
- 生成されたHTMLおよびCSSを使用して非対話型のユーザーインターフェースが表示されます。
- 最後に、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(例:スケルトン、スピナー)を表示し、アクションが完了するとコンポーネントと入れ替えます。
- TypeScript
- JavaScript
import { Suspense } from 'react'
import { PostFeed, Weather } from './Components'
export default function Posts() {
return (
<section>
<Suspense fallback={<p>Loading feed...</p>}>
<PostFeed />
</Suspense>
<Suspense fallback={<p>Loading weather...</p>}>
<Weather />
</Suspense>
</section>
)
}
import { Suspense } from 'react'
import { PostFeed, Weather } from './Components'
export default function Posts() {
return (
<section>
<Suspense fallback={<p>Loading feed...</p>}>
<PostFeed />
</Suspense>
<Suspense fallback={<p>Loading weather...</p>}>
<Weather />
</Suspense>
</section>
)
}
Suspenseを使用することで以下の利点を得ることができます:
- ストリーミングサーバーレンダリング - HTMLをサーバーからクライアントに順次レンダリングする
- 選択的ハイドレーション - Reactはユーザーのインタラクションに基づいてどのコンポーネントを最初に対話可能にするかを優先します
より多くのSuspenseの例と使用ケースについては、Reactのドキュメントをご覧ください。
SEO
- Next.jsは、
generateMetadata
内でのデータ取得が完了するのを待ってから、UIをクライアントにストリーミングします。これにより、ストリーミングされた応答の最初の部分に<head>
タグが含まれることが保証されます。 - ストリーミングはサーバーレンダリングされるため、SEOには影響しません。Googleのリッチリザルトテストツールを使用して、ページがGoogleのウェブクローラーにどのように表示されるかを確認し、シリアル化されたHTMLを表示できます(出典)。
ステータスコード
ストリーミング時に200
ステータスコードが返され、リクエストが成功したことを示します。
サーバーはストリーミングされたコンテンツ内でクライアントにエラーや問題を引き続き通知できます、例えば、redirect
やnotFound
を使用した場合などです。レスポンスヘッダーはすでにクライアントに送信されているため、レスポンスのステータスコードを更新することはできません。これはSEOに影響しません。