Fix guides / Next.js build: "useSearchParams() should be wrapped in a suspense boundary"
How to fix "useSearchParams() should be wrapped in a suspense boundary"
npm run dev works fine. Then the build on Vercel, Netlify, or your own machine stops with useSearchParams() should be wrapped in a suspense boundary at page "/login" and Error occurred prerendering page. This is common in v0 and Cursor projects that use the App Router.
Symptoms
- The build log shows
useSearchParams() should be wrapped in a suspense boundary at page "/something", often followed byExport encountered errors. - The dev server never complains, because it does not prerender pages.
- The page reads something like
?redirect=,?code=, or?plan=from the URL.
Why it happens
During the build, Next.js tries to prerender pages as static HTML. Query parameters only exist when a real visitor arrives, so a component that calls useSearchParams() cannot be rendered at build time. Next.js needs a <Suspense> boundary so it can prerender everything around that component and fill the component in later in the browser. Without one, it refuses to build.
Fix 1 (most common): move the hook into a child and wrap it
Before, all in app/login/page.tsx:
'use client';
import { useSearchParams } from 'next/navigation';
export default function LoginPage() {
const params = useSearchParams();
const next = params.get('next') ?? '/dashboard';
return <LoginForm next={next} />;
}
After:
// app/login/page.tsx (no 'use client' needed here)
import { Suspense } from 'react';
import LoginInner from './LoginInner';
export default function LoginPage() {
return (
<Suspense fallback={<p>Loading...</p>}>
<LoginInner />
</Suspense>
);
}
// app/login/LoginInner.tsx
'use client';
import { useSearchParams } from 'next/navigation';
export default function LoginInner() {
const next = useSearchParams().get('next') ?? '/dashboard';
return <LoginForm next={next} />;
}
Keep the boundary as close to the hook as you can, so the rest of the page still prerenders.
Fix 2: read the params on the server instead
Pages in the App Router receive searchParams as a prop, so you may not need the hook at all. In recent Next.js versions it is a Promise:
export default async function LoginPage({ searchParams }) {
const { next } = await searchParams;
return <LoginForm next={next ?? '/dashboard'} />;
}
This makes the page render per request (dynamic), which is fine for login and checkout pages.
Check every page the build names
The build stops at the first failing page. After fixing one, run npm run build locally again; there may be another page (or a shared component like a navbar) using the same hook. A navbar that calls useSearchParams() breaks every page that includes it, so wrap it once in the layout.
Do not just force everything dynamic
Adding export const dynamic = 'force-dynamic' to the root layout hides the error but turns off static rendering for the whole site, which makes it slower. Use the targeted fixes above.
Request a fix
Last updated 2026-10-19.