Next.js で SSR を無効化する方法(next/dynamic)

Next.js で SSR を無効化する方法(next/dynamic)

例えば Next.js でこんなコンポーネントを表示しようとしても、

const Component = () => {
  return <p>This is {window.location.href}</p>;
};

ReferenceError: window is not defined になります。

これを回避するには、useEffect の中で window オブジェクトにアクセスする必要があります。回避と書きましたがこれが正しい対応方法です。

でも、そもそもこの部分は CSR 前提で作っているから SSR は使わないのよ、と言う場合は SSR を無効化することもできます。

next/dynamic の Dynamic Import を使いましょう #

こんな感じでコンポーネントが export されているとします。

components/index.tsx

const Component = () => {
  return <p>This is {window.location.href}</p>;
};

export { Component };

変更前 #

それを index ページが import して表示しているとします。このままだと先程のエラーが出てしまいます。

pages/index.tsx

import { Component } from "src/components";

export default Component;

変更後 #

では index ページを SSR しないように設定します。それが以下です。

pages/index.tsx

import dynamic from "next/dynamic";

export default dynamic(
  async () => {
    const module = await import("src/components");
    return module.Component;
  },
  { ssr: false },
);

こうすると dynamic import されたコンポーネントは必ず CSR でのみ実行されます。

useEffect を挟まずとも window オブジェクトにアクセス可能です。他には Next.js の router.query が初回に undefined を返すこともなくなります。一発でクエリストリングが取得できます。

dynamic import はコンポーネント単位で行えるため、ページの一部分のみを dynamic import することでその部分だけを SSR 無効化させることも可能です。

アプリ全体を CSR 化してしまうなら #

アプリ全体を一括で CRS 化するのであれば、CSR 用の高階コンポーネントを作成して _app.tsx で他のコンポーネントを囲ってしまうのが楽です。

components/CSRInner.tsx

const CSRInner = ({ children }: { children: JSX.Element | JSX.Element[] }) => {
  return <>{children}</>;
};

export default CSRInner;

components/CSR.tsx

import dynamic from "next/dynamic";

const CSR = dynamic(() => import("./CSRInner"), { ssr: false });

export default CSR;

pages/_app.tsx

import CSR from "src/components/CSR";
import type { AppProps } from "next/app";

const App = ({ Component, pageProps }: AppProps) => {
  return (
    <CSR>
      <Component {...pageProps} />
    </CSR>
  );
};

export default App;

リファレンス #

公式のわかりやすいドキュメントがありますので、後の詳細はそちらをぜひご参照ください。

Advanced Features: Dynamic Import | Next.js