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 に直接書いていた場合に頻発する。
よくある原因
- Pages Router の感覚で
page.tsx(既定でサーバーコンポーネント)に直接dynamicを書いた - 取り込みたいコンポーネントが
window依存で SSR できない - クライアントツリーとサーバーツリーの境界が曖昧で、
use clientの置き場所が定まらない - 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 公式ドキュメント を参照。