vercel/next.js · error · BailoutToCSRError

${expression}

Error message

${expression}

What it means

This error is thrown when useSearchParams() is called during static prerendering of a Server Component in the App Router. Next.js requires access to search params to be dynamic (request-time); during a static build there is no search params value available, so the renderer throws a BailoutToCSRError with the expression string (e.g. `useSearchParams()`) to signal that this tree must fall back to client-side rendering or be wrapped in a Suspense boundary.

Source

Thrown at packages/next/src/server/app-render/dynamic-rendering-client.ts:116

    case 'prerender-client': {
      React.use(
        makeClientHookHangingPromise(
          workUnitStore.renderSignal,
          new ClientHookDynamicError(workStore.route, expression)
        )
      )
      break
    }
    case 'prerender-legacy': {
      if (workStore.forceStatic) {
        return
      }
      if (process.env.__NEXT_EXPERIMENTAL_REACT_BROWSER_BAILOUT) {
        // @ts-expect-error TODO: Update @types/react-dom to include the reason argument.
        React.use(browser(getUseSearchParamsBailoutReason))
        return
      } else {
        throw new BailoutToCSRError(expression)
      }
    }
    case 'prerender':
    case 'prerender-runtime':
      throw new InvariantError(
        `\`${expression}\` was called from a Server Component. Next.js should be preventing ${expression} from being included in server components statically, but did not in this case.`
      )
    case 'cache':
    case 'unstable-cache':
    case 'private-cache':
      throw new InvariantError(
        `\`${expression}\` was called inside a cache scope. Next.js should be preventing ${expression} from being included in server components statically, but did not in this case.`
      )
    case 'generate-static-params':
      throw new InvariantError(
        `\`${expression}\` was called in \`generateStaticParams\`. Next.js should be preventing ${expression} from being included in server component files statically, but did not in this case.`
      )
    case 'request':

View on GitHub (pinned to 89d017eac4)

Solutions

  1. Wrap the component calling useSearchParams() in a <Suspense> boundary (e.g. <Suspense fallback={...}><SearchBar /></Suspense>)
  2. Move useSearchParams() into a dedicated client component ('use client') and isolate it under Suspense
  3. Add export const dynamic = 'force-dynamic' to the route if it genuinely needs request-time rendering
  4. For static export, replace useSearchParams() with reading window.location.search in useEffect, or use next/navigation router handling after mount

Example fix

// before
export default function Page() {
  return <SearchBar /> // SearchBar calls useSearchParams()
}

// after
import { Suspense } from 'react'
export default function Page() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <SearchBar />
    </Suspense>
  )
}
Defensive patterns

Strategy: validation

Validate before calling

import { Suspense } from 'react'
// Before shipping, verify every useSearchParams() consumer is isolated:
// grep -rn "useSearchParams" app/ components/
// and statically confirm each usage site is inside a <Suspense> ancestor or force-dynamic route.

Type guard

function isStaticRoute(config: { dynamic?: string }): boolean {
  return config.dynamic !== 'force-dynamic'
}

Try / catch

// Not applicable: this is a build-time prerender bailout, not a runtime exception to catch.

Prevention

When it happens

Trigger: Calling useSearchParams() in a component that is statically prerendered (next build without dynamic = 'force-dynamic') and is NOT wrapped in a <Suspense> boundary; importing a client component using useSearchParams into a statically rendered page layout; using a shared header/navbar reading search params in the root layout during static export.

Common situations: Upgrading Next.js versions where previously-tolerated unwrapped useSearchParams became a build error; static export (output: 'export') with a component reading search params; adding a search-param-aware widget to a layout; missing Suspense boundary around the client component.

Related errors


AI-assisted analysis of vercel/next.js@89d017eac4 (2026-08-27). Data as JSON: /api/errors/ab41482c692de754. Report an issue: GitHub.