Next.js "useSearchParams() should be wrapped in a suspense boundary" in CI
In the App Router, useSearchParams() opts a component into client-side reading of the URL. During static prerendering Next.js requires a Suspense boundary above it, otherwise the page deopts to client rendering and the build fails the check.
What this error means
next build prints "Error occurred prerendering page" for a route and "useSearchParams() should be wrapped in a suspense boundary at page '/path'", failing the build.
Error occurred prerendering page "/search". Read more:
https://nextjs.org/docs/messages/prerender-error
Error: useSearchParams() should be wrapped in a suspense boundary at page "/search".
Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailoutCommon causes
useSearchParams is used without a Suspense boundary
A client component reads search params high in the tree with no <Suspense> ancestor, so static generation cannot bail out cleanly.
The hook sits directly in the page component
Calling the hook in the page itself, rather than a child wrapped in Suspense, forces the whole route to deopt during prerender.
How to fix it
Wrap the consumer in Suspense
- Move the component that calls
useSearchParams()into its own child. - Render that child inside a
<Suspense>boundary with a fallback. - Re-run next build to confirm the route prerenders.
import { Suspense } from 'react'
import Results from './results' // calls useSearchParams()
export default function Page() {
return (
<Suspense fallback={<p>Loading...</p>}>
<Results />
</Suspense>
)
}Opt the route out of static generation if appropriate
If the page is inherently dynamic, force dynamic rendering so prerendering does not run.
export const dynamic = 'force-dynamic'How to prevent it
- Isolate hooks that read the URL into Suspense-wrapped children.
- Reserve
force-dynamicfor routes that genuinely cannot be static. - Run a production build in CI on every PR to catch prerender deopts.