Next.jsで「useSearchParams() should be wrapped in a suspense boundary」エラーを修正する方法

intermediate⚛️ React2026-05-02| Next.js 13/14/15 (App Router), React 18+, Node.js 18.x 以上

Error Message

Error: useSearchParams() should be wrapped in a suspense boundary at page "/search". Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout
#react#nextjs#suspense#usesearchparams

本番ビルドの壁ローカル開発環境(npm run dev)では、検索バーは問題なく動作します。しかし、next buildを実行したり、Vercelにデプロイしたりすると状況が一変します。突然、「CSR bailout」エラーが発生してプロセスがクラッシュし、デプロイが中断されてしまいます。

Error: useSearchParams() should be wrapped in a suspense boundary at page "/search".

これは、?q=javascriptのような検索パラメータがブラウザ上にのみ存在するために起こります。Next.jsはビルド時に静的なHTMLを生成しようとしますが、そのデータが存在すべき場所に「穴」が開いた状態になります。プリレンダリング段階で、この欠落した情報をどのように扱うかの計画が必要なのです。

静的HTMLの「穴」Next.jsは、ページをユーザーに届ける前に静的なHTMLファイルに変換することで高速化を図っています。コンポーネントがuseSearchParams()を呼び出すと、ブラウザ専用の情報を要求することになります。Suspense境界がないと、Next.jsはどう処理すべきか判断できず、その特定のコンポーネントの静的レンダリングを完全に中止(bailout)してしまいます。

この「bailout(脱落)」は連鎖することが多く、ページ全体がクライアントサイドレンダリングに依存せざるを得なくなり、Largest Contentful Paint (LCP) スコアを悪化させます。<Suspense>を使用することで、エンジンに対して「ページの残りの部分はHTMLとして書き出してください。この特定の部分については、ブラウザが処理を引き継ぐまでフォールバックを表示します」と伝えることができます。

解決策:分離とラップページファイルのトップレベルでuseSearchParams()を呼び出さないでください。代わりに、動的なロジックを「末端(leaf)」のコンポーネントに移動します。これにより、不確実な要素を封じ込め、ページの他の部分を静的なまま保つことができます。

1. 検索コンテンツコンポーネントの作成実際にURLにアクセスするUI部分を分離します。

// components/SearchContent.tsx
'use client'

import { useSearchParams } from 'next/navigation'

export default function SearchContent() {
  const searchParams = useSearchParams()
  const query = searchParams.get('q')

  return (
    
      検索結果: {query || 'すべてのアイテム'}
    
  )
}

2. Suspense境界の追加メインのpage.tsxで、新しいコンポーネントをラップします。これによりコンパイラの条件が満たされ、ユーザーエクスペリエンスも向上します。

// app/search/page.tsx
import { Suspense } from 'react'
import SearchContent from '@/components/SearchContent'

export default function SearchPage() {
  return (
    
      
        検索結果
        
          
        
      
    
  )
}

Layoutsでの注意点グローバルなlayout.tsxuseSearchParams()を追加してしまうのは、よくある罠です。これにより、意図せずアプリケーション全体が巨大なクライアントサイドアプリになってしまう可能性があります。ナビゲーションバーでURLが必要な場合は、検索入力フィールドや特定のインタラクティブな要素のみをSuspenseでラップしてください。これにより、周囲のヘッダーやブランディング部分は完全に静的な状態を維持できます。

検証:シンボルの確認npm run buildを実行し、ターミナルの出力を注意深く確認してください。Next.jsが/searchルートをどのように分類したかを確認します。以下のインジケーターを探してください:

  • λ (Lambda): ページが動的であることを意味します。検索パラメータを使用する場合、これが期待される状態です。- ○ (Circle): ページが静的であることを意味します。検索ページにこれが表示されている場合は、Suspense境界が実際にフックをキャッチしているか確認してください。最後に、UIをテストします。Chrome DevToolsでネットワークを「Slow 3G」に制限してください。「結果を取得中...」というメッセージが一瞬表示されれば、修正は完璧に機能しています。

Related Error Notes