Demo, all content is generated
Question

Build fails on Vercel: useSearchParams() should be wrapped in a suspense boundary at page /login

Solved · 522 views · asked by harriet_g · edited

npm run dev works perfectly. Vercel build dies with:

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

Error occurred prerendering page "/login".

The login page reads ?next=/dashboard to redirect after login. The whole page.tsx is "use client".

What I’ve tried

Claude wrapped the whole page return in <Suspense> inside the same component. Same error.

Comment
Same error on my /search page yesterday, following this. lucy_hart · edited

3 answers

Marked as helpful by the asker
dev_ana · edited

Suspense has to be above the component that calls useSearchParams, not inside it. Split it:

// app/login/page.tsx (server component, no "use client")
import { Suspense } from "react";
import LoginForm from "./login-form";

export default function Page() {
  return (
    <Suspense fallback={null}>
      <LoginForm />
    </Suspense>
  );
}

login-form.tsx gets the "use client" and the useSearchParams call. Dev doesn't catch this because it renders on request; only the production build tries to prerender the page statically.

Alternative: page.tsx receives searchParams as a prop (it's a promise in Next 15, so await it) and passes next down. No client hook needed at all.

Comment
Split it into two files, build is green. The 'dev renders on request' explanation finally made the dev vs build difference click for me. harriet_g · edited
Clearest explanation of this error I've read. tom_brewer · edited
tom_brewer · edited

If the error says at page "/_not-found" instead of your own page, the hook is in something your root layout renders (a navbar with a search box, usually). Same fix, wrap that component in Suspense in the layout.

Comment
mei_lin · edited

+1 to the searchParams prop route. For a login redirect you don't need client-side reactivity, reading it on the server is simpler and nothing to wrap.

Comment