Next.jsでシングルページアプリケーション (SPA) を構築する方法
Next.jsはシングルページアプリケーション (SPA) の構築を完全にサポートしています。
これには、プリフェッチによる高速なルート遷移、クライアントサイドデータフェッチ、ブラウザAPIの使用、サードパーティクライアントライブラリとの統合、静的ルートの作成などが含まれます。
既存のSPAがある場合、コードに大きな変更を加えることなくNext.jsに移行できます。Next.jsでは、必要に応じてサーバー機能を段階的に追加することが可能です。
シングルページアプリケーション (SPA) とは?
SPAの定義は様々です。ここでは「厳密なSPA」を以下のように定義します:
- クライアントサイドレンダリング (CSR): アプリは1つのHTMLファイル(例:
index.html
)で提供されます。すべてのルート、ページ遷移、データフェッチはブラウザ上のJavaScriptによって処理されます。 - フルページリロードなし: 各ルートに対して新しいドキュメントを要求する代わりに、クライアントサイドJavaScriptが現在のページのDOMを操作し、必要に応じてデータをフェッチします。
厳密なSPAでは、ページがインタラクティブになる前に大量のJavaScriptを読み込む必要がある場合が多く、クライアントデータのウォーターフォール管理も課題になりがちです。Next.jsでSPAを構築することでこれらの問題に対処できます。
Next.jsでSPAを構築する理由
Next.jsはJavaScriptバンドルの自動コード分割が可能で、異なるルートへの複数のHTMLエントリーポイントを生成します。これにより、クライアントサイドで不要なJavaScriptコードを読み込む必要がなくなり、バンドルサイズが縮小され、ページ読み込みが高速化されます。
next/link
コンポーネントはルートを自動的にプリフェッチするため、厳密なSPAのような高速なページ遷移を実現しつつ、アプリケーションのルーティング状態をURLに保持してリンクや共有が可能です。
Next.jsは静的サイトや、すべてがクライアントサイドでレンダリングされる厳密なSPAとして始めることができます。プロジェクトが成長した場合、必要に応じてReact Server Components、Server Actionsなどのサーバー機能を段階的に追加できます。
例
SPA構築でよく使われるパターンと、Next.jsでの解決方法を見ていきましょう。
Context Provider内でのReactのuse
使用
親コンポーネント(またはレイアウト)でデータをフェッチし、Promiseを返してから、Reactのuse
フックでクライアントコンポーネント内で値をアンラップすることを推奨します。
Next.jsはサーバー上で早期にデータフェッチを開始できます。この例では、アプリケーションのエントリーポイントであるルートレイアウトです。サーバーはすぐにクライアントへのレスポンスのストリーミングを開始できます。
データフェッチをルートレイアウトに「引き上げる」ことで、Next.jsはアプリケーション内の他のコンポーネントよりも前にサーバー上で指定されたリクエストを開始します。これによりクライアントウォーターフォールが解消され、クライアントとサーバー間の複数ラウンドトリップが防止されます。サーバーがデータベースに近い(理想的には同一場所にある)ため、パフォーマンスが大幅に向上する可能性もあります。
例えば、Promiseを呼び出すようにルートレイアウトを更新しますが、awaitはしません。
単一のPromiseを遅延して渡すことはできますが、一般的にこのパターンはReactコンテキストプロバイダーと組み合わせて使用されます。これにより、カスタムReactフックを使用してクライアントコンポーネントから簡単にアクセスできます。
PromiseをReactコンテキストプロバイダーに転送できます:
最後に、任意のクライアントコンポーネントでuseUser()
カスタムフックを呼び出し、Promiseをアンラップできます:
Promiseを消費するコンポーネント(上記のProfile
など)はサスペンドされます。これにより部分的なハイドレーションが可能になります。JavaScriptの読み込みが完了する前に、ストリーミングされプリレンダリングされたHTMLを確認できます。
SWRを使用したSPA
SWRはデータフェッチ用の人気Reactライブラリです。
SWR 2.3.0(およびReact 19+)では、既存のSWRベースのクライアントデータフェッチコードと並行してサーバー機能を段階的に採用できます。これは上記のuse()
パターンの抽象化です。つまり、データフェッチをクライアントとサーバーサイド間で移動したり、両方を使用したりできます:
- クライアントのみ:
useSWR(key, fetcher)
- サーバーのみ:
useSWR(key)
+ RSC提供データ - 混合:
useSWR(key, fetcher)
+ RSC提供データ
例えば、アプリケーションを<SWRConfig>
とfallback
でラップします:
これはサーバーコンポーネントなので、getUser()
は安全にクッキーやヘッダーを読み取ったり、データベースと通信したりできます。別のAPIルートは必要ありません。<SWRConfig>
以下のクライアントコンポーネントは、同じキーでuseSWR()
を呼び出してユーザーデータを取得できます。useSWR
を使用するコンポーネントコードは、既存のクライアントフェッチソリューションから変更する必要はありません。
fallback
データはプリレンダリングされ、初期HTMLレスポンスに含まれ、useSWR
を使用する子コンポーネントですぐに読み取られます。SWRのポーリング、再検証、キャッシュは引き続きクライアントサイドのみで実行されるため、SPAに必要なすべてのインタラクティビティが維持されます。
初期のfallback
データはNext.jsによって自動的に処理されるため、以前はdata
がundefined
かどうかをチェックするために必要な条件付きロジックを削除できます。データが読み込み中の場合、最も近い<Suspense>
境界がサスペンドされます。
SWR | RSC | RSC + SWR | |
---|---|---|---|
SSRデータ | |||
SSR中のストリーミング | |||
リクエストの重複排除 | |||
クライアント機能 |
React Queryを使用したSPA
React QueryをNext.jsでクライアントとサーバーの両方で使用できます。これにより、厳密なSPAを構築できるだけでなく、Next.jsのサーバー機能をReact Queryと組み合わせて利用できます。
詳細はReact Queryドキュメントを参照してください。
ブラウザのみでのコンポーネントレンダリング
クライアントコンポーネントはnext build
中にプリレンダリングされます。クライアントコンポーネントのプリレンダリングを無効にし、ブラウザ環境でのみ読み込みたい場合は、next/dynamic
を使用できます:
これはwindow
やdocument
などのブラウザAPIに依存するサードパーティライブラリに便利です。また、これらのAPIの存在をチェックするuseEffect
を追加し、存在しない場合はプリレンダリングされるnull
やローディング状態を返すこともできます。
クライアントでのシャロールーティング
Create React AppやViteなどの厳密なSPAから移行する場合、URL状態を更新するためにシャロールーティングする既存のコードがあるかもしれません。これは、デフォルトのNext.jsファイルシステムルーティングを使用せずに、アプリケーション内のビュー間で手動遷移を行うのに便利です。
Next.jsでは、ネイティブのwindow.history.pushState
とwindow.history.replaceState
メソッドを使用して、ページをリロードせずにブラウザの履歴スタックを更新できます。
pushState
とreplaceState
の呼び出しはNext.jsルーターと統合され、usePathname
やuseSearchParams
との同期が可能です。
Next.jsでのルーティングとナビゲーションの仕組みについて詳しく学べます。
クライアントコンポーネントでのServer Actionsの使用
クライアントコンポーネントを使用しながらも、Server Actionsを段階的に採用できます。これにより、APIルートを呼び出すためのボイラープレートコードを削除し、代わりにuseActionState
などのReact機能を使用してローディングやエラー状態を処理できます。
例えば、最初のServer Actionを作成します:
クライアントからServer Actionをインポートして使用でき、JavaScript関数を呼び出すのと同様です。手動でAPIエンドポイントを作成する必要はありません:
Server Actionsでのデータ変更について詳しく学べます。
静的エクスポート(オプション)
Next.jsは完全な静的サイトの生成もサポートしています。これには厳密なSPAに比べていくつかの利点があります:
- 自動コード分割: 単一の
index.html
を配信する代わりに、Next.jsはルートごとにHTMLファイルを生成するため、訪問者はクライアントJavaScriptバンドルを待たずにコンテンツをより速く取得できます。 - ユーザー体験の向上: すべてのルートに対して最小限のスケルトンを提供する代わりに、各ルートに対して完全にレンダリングされたページを取得します。ユーザーがクライアントサイドでナビゲートする場合、遷移は瞬時でSPAのようなままです。
静的エクスポートを有効にするには、設定を更新します:
next build
を実行後、Next.jsはアプリケーションのHTML/CSS/JSアセットを含むout
フォルダを作成します。
注: 静的エクスポートではNext.jsサーバー機能はサポートされていません。詳細
既存プロジェクトのNext.jsへの移行
次のガイドに従ってNext.jsに段階的に移行できます:
すでにPages RouterでSPAを使用している場合、App Routerの段階的採用方法を学べます。