Next.js で「useSearchParams() should be wrapped in a suspense boundary」が解消できない
App Router で useSearchParams() を Suspense 境界なしに使うと、ビルド(静的生成)時にこのエラーで失敗する。
呼び出す部分を <Suspense> で包めば解決する。
公開: 更新:
要約
App Router の useSearchParams() は、URL のクエリに依存するためクライアントでしか確定しません。
Suspense で囲まずに使うと、ページ全体がクライアント描画(CSR)に倒れてしまうため、Next.js はビルド時にこのエラーで止めます。
解決は「使う場所だけを <Suspense> で囲む」ことです。
実行例
useSearchParams() を呼ぶ SearchBox を <Suspense> で包まずにページへ置くと、next build は静的ページの生成中に useSearchParams() should be wrapped in a suspense boundary at page "/" を出して終了コード 1 で止まった。<Suspense> で包むとビルドが通って / は静的ページ(○)のまま生成され、包まずに dynamic = 'force-dynamic' を置いた場合もビルドは通り、/ はリクエスト時に描画される動的ページ(ƒ)になっている。
$ npx next build
▲ Next.js 15.5.25
Creating an optimized production build ...
✓ Compiled successfully in 2.0s
Linting and checking validity of types ...
Collecting page data ...
Generating static pages (0/4) ...
⨯ useSearchParams() should be wrapped in a suspense boundary at page "/". Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout
at g (/tmp/tmp.HZt9P5nSW5/.next/server/chunks/833.js:21:14887)
at m (/tmp/tmp.HZt9P5nSW5/.next/server/chunks/833.js:1:30399)
at f (/tmp/tmp.HZt9P5nSW5/.next/server/app/page.js:2:7712)
at n3 (/tmp/tmp.HZt9P5nSW5/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:2:82831)
at n6 (/tmp/tmp.HZt9P5nSW5/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:2:84601)
at n6 (/tmp/tmp.HZt9P5nSW5/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:2:101560)
at n5 (/tmp/tmp.HZt9P5nSW5/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:2:104801)
at n7 (/tmp/tmp.HZt9P5nSW5/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:2:102219)
at ia (/tmp/tmp.HZt9P5nSW5/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:2:108211)
at ie (/tmp/tmp.HZt9P5nSW5/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:2:106833)
Error occurred prerendering page "/". Read more: https://nextjs.org/docs/messages/prerender-error
Export encountered an error on /page: /, exiting the build.
⨯ Next.js build worker exited with code: 1 and signal: null
$ echo $?
1$ npx next build
▲ Next.js 15.5.25
Creating an optimized production build ...
✓ Compiled successfully in 2.1s
Linting and checking validity of types ...
Collecting page data ...
Generating static pages (0/4) ...
Generating static pages (1/4)
Generating static pages (2/4)
Generating static pages (3/4)
✓ Generating static pages (4/4)
Finalizing page optimization ...
Collecting build traces ...
Route (app) Size First Load JS
┌ ○ / 287 B 103 kB
└ ○ /_not-found 993 B 103 kB
+ First Load JS shared by all 102 kB
├ chunks/255-37e0f0325134c4d7.js 46.4 kB
├ chunks/4bd1b696-c023c6e3521b1417.js 54.2 kB
└ other shared chunks (total) 1.88 kB
○ (Static) prerendered as static content
$ echo $?
0$ npx next build
▲ Next.js 15.5.25
Creating an optimized production build ...
✓ Compiled successfully in 1986ms
Linting and checking validity of types ...
Collecting page data ...
Generating static pages (0/3) ...
✓ Generating static pages (3/3)
Finalizing page optimization ...
Collecting build traces ...
Route (app) Size First Load JS
┌ ƒ / 287 B 103 kB
└ ○ /_not-found 993 B 103 kB
+ First Load JS shared by all 102 kB
├ chunks/255-37e0f0325134c4d7.js 46.4 kB
├ chunks/4bd1b696-c023c6e3521b1417.js 54.2 kB
└ other shared chunks (total) 1.88 kB
○ (Static) prerendered as static content
ƒ (Dynamic) server-rendered on demand
$ echo $?
0— 2026-09-14 時点の出力
検証環境
- 検証日
- 実行環境
node:20Debian GNU/Linux 12 (bookworm)- バージョン
- Node.js 20.20.2
- npm 10.8.2
- Python 3.11.2
- Git 2.39.5
この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。
よくある原因
useSearchParams()を呼ぶ Client Component が<Suspense>の外にある。- ページ全体が CSR にフォールバックするのを Next.js が防いでいる。
- ルートがビルド時に静的生成されるため、ビルドの段階で検出される。
output: 'export'を指定していなくても、既定で静的に事前レンダリングされるページでは同じエラーになる。
解決策
1. Suspense で包む
クエリを読む部分を小さなコンポーネントに分け、<Suspense> で囲みます。
静的シェルが保たれます。
import { Suspense } from 'react';
import { SearchBox } from './search-box';
export default function Page() {
return (
<Suspense fallback={<p>読み込み中…</p>}>
<SearchBox />
</Suspense>
);
}'use client';
import { useSearchParams } from 'next/navigation';
export function SearchBox() {
const q = useSearchParams().get('q');
return <input defaultValue={q ?? ''} />;
}2. 動的描画に切り替える
静的生成が不要なら、Server Component で export const dynamic = 'force-dynamic' を置くか、connection() を呼んでオンデマンド描画にします。
この場合 Suspense は不要です。