できない.dev

Next.js App Router でサーバーコンポーネントから dynamic ssr:false が使えない

App Router のサーバーコンポーネント内で next/dynamic に ssr: false を渡すとビルドエラーになる。
ssr: false はクライアントコンポーネントの中でのみ指定でき、用途が SSR off ならクライアントラッパ、遅延ロードだけなら Suspense で分離する。

#nextjs#dynamic-import#ssr#app-router#client-component

公開: 更新:

要約

App Router で、サーバーコンポーネントから dynamic(..., { ssr: false }) を呼ぶとビルド時に「ssr: false is not allowed with next/dynamic in Server Components」エラーが出る。'use client' を付けた薄いラッパを挟むのが定石。
Pages Router の感覚で page.tsx に直接書いていた場合に頻発する。

よくある原因

  1. Pages Router の感覚で page.tsx(既定でサーバーコンポーネント)に直接 dynamic を書いた
  2. 取り込みたいコンポーネントが window 依存で SSR できない
  3. クライアントツリーとサーバーツリーの境界が曖昧で、use client の置き場所が定まらない
  4. Tree shaking でクライアント側に紛れ込ませようと ssr: false を多用している

解決策

1. クライアントラッパを作る

// app/_components/ChartClient.tsx
"use client";
import dynamic from "next/dynamic";
 
const Chart = dynamic(() => import("./Chart"), { ssr: false });
 
export default function ChartClient(props: ChartProps) {
  return <Chart {...props} />;
}
// app/dashboard/page.tsx (Server Component)
import ChartClient from "../_components/ChartClient";
 
export default function Page() {
  return <ChartClient data={...} />;
}

dynamic 呼び出しが "use client" 配下になることで、ssr: false が有効になる。
詳細は next/dynamic API リファレンス を参照。

2. <Suspense> で代替する

SSR を完全に切る必要がなく「初期 HTML を出さずに遅延ロードしたい」だけなら、ssr: false を外して <Suspense> を使う:

import { Suspense, lazy } from "react";
 
const Heavy = lazy(() => import("./Heavy"));
 
export default function Page() {
  return (
    <Suspense fallback={<Spinner />}>
      <Heavy />
    </Suspense>
  );
}

サーバーで先に fallback の HTML を返し、クライアント側で実体をハイドレートする流れになる。

3. クライアント側に依存ロジックを集約

window.localStorage などブラウザ API が必要な処理は、コンポーネントの内部でも useEffect の中で呼ぶ。
サーバーで実行されない経路にロジックを置けば、そもそも ssr: false を指定する必要がないことが多い。
設計指針は Lazy Loading 公式ドキュメント を参照。

この記事は役立ちましたか?