【Next.js】SSR/SGでブラウザ機能(Cookieなど)を活用する際の注意点

JavaScript

SSR/SGではレンダリング時にブラウザの機能を使えない

サーバサイドでの処理の過程ではクライアントサイド、つまりブラウザ上でのみ実行可能な機能は利用できません。具体例は以下の通りです。

ブラウザ上でのみ実行可能な機能の例
  • Windowの参照
  • Cookieの参照
  • localStrageの参照
  • sessionStorageの参照

Next.jsのページのレンダリングはデフォルトでPre-rendering、つまりSSR(Server-side Rendering)もしくはSG(Static Generation)ですので1Next.jsにおいて以下のコードは意図した結果になりません。

import type { NextPage } from "next";
import Cookies from 'js-cookie'

const Example: NextPage = () => {

  // window.documentによる文書の参照
  const element = window.document.getElementById('example'); // ReferenceError: window is not defined

  // window.alertによる警告ダイアログ表示
  window.alert("Hello world!"); // ReferenceError: window is not defined

  // Cookieのデータ取得(exampleというnameのvalueを取得する場合)
  console.log(Cookies.get('example')); // undefined

  // localStorageのデータ取得(exampleというnameのlocalStrageを取得する場合)
  console.log(localStorage.getItem("example")); // ReferenceError: localStorage is not defined

  // sessionStorageのデータ取得(exampleというnameのsessionStrageを取得する場合)
  console.log(sessionStorage.getItem("example")); // ReferenceError: sessionStorage is not defined

  return <div id='example'>Example</div>;
};

export default Example;

解決方法: SSR/SGでブラウザの機能を活用したい場合は副作用で実行する

副作用とは「JSXあるいはTSXからUIを構築する処理」以外に関する処理のことをいいます。

副作用の実行は、関数コンポーネントであれば副作用フックと呼ばれるuseEffectuseSWR、クラスコンポーネントであればcomponentDidMountcomponentDidUpdatecomponentWillUnmountで行います。

副作用はマウント後に実行されるため、Pre-renderingのページであってもブラウザ上でのみ実行可能な機能を利用できます。具体例は以下の通りです。

import type { NextPage } from "next";
import Cookies from 'js-cookie'
import { useEffect } from "react";

const Example: NextPage = () => {

  useEffect(() => {
    // window.documentによる文書の参照
    const element = window.document.getElementById('example');
    console.log(element?.innerText); // Example

    // window.alertによる警告ダイアログ表示
    window.alert("Hello world!"); // 警告ダイアログが表示される

    // Cookieのデータ取得
    Cookies.set('example', 'exampleCookie');
    console.log(Cookies.get('example')); // exampleCookie

    // localStorageのデータ取得
    localStorage.setItem('example', 'exampleLocalStrage');
    console.log(localStorage.getItem("example")); // exampleLocalStrage

    // sessionStrageのデータ取得
    sessionStorage.setItem('example', 'exampleSessionStrage');
    console.log(sessionStorage.getItem("example")); // exampleSessionStrage
  }, []);

  return <div id='example'>Example</div>;
};

export default Example;

なお、Next.jsでブラウザの機能を利用する場合は副作用以外にtypeof windowprocess.browserdynamic importを活用する方法もあります。
詳細解説は Next.jsのSSR回避(クライアントサイドでのみ実行)の実装パターン集 で紹介しています。

さいごに

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!