はじめに
Next.jsでは、サーバーサイドでレンダリングすることを SSR (Server Side Rendering) 、クライアントサイドでレンダリングすることを CSR (Client Side Rendering) と呼びます。プロダクトの要件によっては、どうしてもSSRが必要な場合があります。
以下にその例を挙げます。
- 秘匿情報の取り扱い: APIキーなど、表には出したくない情報をサーバ側で処理したい
- SEO効果: SSRすることで、検索エンジン向けに最適化されたページを提供できる
今回は、この2つのケースに対して、Next.jsでどのように対応できるかを説明します。
Next.jsでSSRとCSRを切り替える方法
方法はすごくシンプルです。
CSRしたいコンポーネントファイル先頭に'use client'
を記述するだけです。
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
'use client'
を記述したコンポーネントを呼び出すまでのコンポーネントはSSR、記述したコンポーネント以降は全てCSRでレンダリングされます。
自作サイトの以下「和やか投票箱」を例に説明したいと思います。
TOPページ表示時のコンポーネントは以下の順で読み込まれています。
それぞれのコンポーネントの役割とレンダリング元は以下のようになっています。
- Layout (SSR)
- ヘッダーやフッター等全ページ共通部品を定義するために利用
- 特にクライアントサイドで動く動的な値を読み込んでいないため、SSR処理
- Session (SSR)
- cookieにユーザ識別のためのセッションが存在するか確認
- セッション確認のために
next/headers
を利用しており、サーバサイドでしか動かないためSSR処理
- SessionStore (CSR)
- SessionコンポーネントでSessionを確認した際、セッションの存在が確認できなかった場合にセッションを付与
- headerにCookieがセットされた状態で返却されるセッション付与API宛に問い合わせるため、呼び出し元がクライアントサイドでないとCookieがセットできないため、CSR処理
- Page (CSR)
- Pageコンポーネントの呼び出し元であるSessionStoreコンポーネントがCSRのため、このコンポーネントもCSRとなる
- その他、クライアントサイドで「選択肢を追加」「投票箱を作成」等のボタンが押された時の状態遷移が存在するため、どちらにせよCSR処理が必要
もしクライアントサイドでは秘匿にしたい値がある場合、Sessionコンポーネントまでのコンポーネントに記述しておけばサーバサイドで処理してくれるので、最初の例に挙げた「秘匿情報の取り扱い」の要件が解決できます。
クライアントコンポーネントでもSSRしたい
最初の例に挙げた「SEO効果」の要件を解決するには、Pageコンポーネントにある内容をSSRした状態でクライアントに返さなければいけませんが、クライアントコンポーネントにせざるを得ない要因が複数あります。
- 親コンポーネントがCSRのため、子もÇSRになる
- クライアントサイドで動的に動く値があるため、
useState
,useEffect
等クライアントサイドでしか動かない関数がある
本来ならクライアントコンポーネントであればJavascriptを止めると描画されないはずですが、試しに「和やか投票箱」をJavascriptを止めて開いてみてください。
なんとページが描画されます...!
描画予定のbodyの内容はPageコンポーネントに集約しており、Pageコンポーネントはクライアントコンポーネントのため、クライアントコンポーネントがもしクライアントサイドでしか描画できないならヘッダーとフッターしか描画されないはずです。
このようになる理由は公式ドキュメントに記載があります。
To optimize the initial page load, Next.js will use React's APIs to render a static HTML preview on the server for both Client and Server Components. This means, when the user first visits your application, they will see the content of the page immediately, without having to wait for the client to download, parse, and execute the Client Component JavaScript bundle.
要約すると、クライアントコンポーネントであってもサーバでレンダリングできるものはサーバ上でレンダリングするといったないようになります。
しかしそうであれば、ここで一つ疑問に思うことがあります。useState
を利用している等クライアントサイドで値が変わることで表示が変わる部分は、初期描画ではどうなるのでしょうか。
その疑問を解決すべく、以下定義のページをJavascript無しで開いてみます。
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
0が表示されました
ではuseStateの初期値5
ならどうでしょうか。
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(5)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
5になりました!!
つまり、useState
の初期値で定義した値でSSRしてくれるようです。
この方法を利用すれば、最初の例に挙げた「SEO効果」の要件が解決できそうです!
最後に
今回の解説では、Next.js13において、SSRをうまく利用する方法について説明しました。
要点は以下の通りです。
- SSRとCSRの切り替えは、'use client'を記述するだけで簡単にクライアントサイドレンダリングが可能です
- useStateなどのクライアントサイドロジックもSSRで初期値を反映するため、初回表示時に必要な情報をサーバーサイドで準備できます
Next.js13のSSR機能を適切に使うことで、クライアントサイドのインタラクティブな要素を活かしながらも、SEOに強いページを構築できるので、ぜひ活用してみてください。