This error occurs when trying to use the useSearchParams hook in a Next.js Server Component. The hook is designed exclusively for Client Components to prevent stale values during partial rendering and maintain proper hydration.
The useSearchParams hook is a Client Component-only feature in Next.js that provides access to the current URL's query string parameters. When you attempt to use this hook in a Server Component, Next.js throws this error to prevent architectural violations. This restriction exists because Server Components render on the server and cannot access browser-specific APIs or maintain reactive state tied to URL changes. The useSearchParams hook needs to react to URL changes in real-time, which requires client-side JavaScript execution. Using it in Server Components would lead to stale values during partial rendering and break Next.js's rendering model. Next.js provides alternative approaches for accessing search parameters in Server Components, primarily through the searchParams prop available in Page components. This ensures proper server-side rendering while maintaining type safety and preventing hydration mismatches.
The simplest fix is to mark your component as a Client Component by adding the "use client" directive at the top of the file:
'use client';
import { useSearchParams } from 'next/navigation';
export default function SearchComponent() {
const searchParams = useSearchParams();
const query = searchParams.get('q');
return <div>Search: {query}</div>;
}This tells Next.js that this component and its imports should run on the client, allowing access to useSearchParams.
If you're in a Page component (Server Component), use the searchParams prop instead of the hook:
// app/search/page.tsx
export default function SearchPage({
searchParams,
}: {
searchParams: { q?: string }
}) {
const query = searchParams.q;
return <div>Search: {query}</div>;
}This approach works in Server Components and enables proper server-side rendering. You can then pass these values down to Client Components as props if needed.
If you're using useSearchParams in a statically rendered route, wrap the component in a Suspense boundary:
// app/page.tsx
import { Suspense } from 'react';
import SearchComponent from '@/components/SearchComponent';
export default function Page() {
return (
<Suspense fallback={<div>Loading...</div>}>
<SearchComponent />
</Suspense>
);
}This allows Next.js to statically render everything above the Suspense boundary while client-rendering the dynamic search params component.
If you have a mixed component, split the search params logic into its own Client Component:
// components/SearchFilter.tsx
'use client';
import { useSearchParams } from 'next/navigation';
export function SearchFilter() {
const searchParams = useSearchParams();
const filter = searchParams.get('filter');
return <div>Filter: {filter}</div>;
}
// app/products/page.tsx (Server Component)
import { SearchFilter } from '@/components/SearchFilter';
export default function ProductsPage() {
return (
<div>
<h1>Products</h1>
<SearchFilter />
</div>
);
}This maintains the benefits of Server Components while isolating client-side search param access.
For better server-side rendering, read params in the Server Component and pass them as props:
// app/search/page.tsx (Server Component)
import { SearchResults } from '@/components/SearchResults';
export default function SearchPage({
searchParams,
}: {
searchParams: { q?: string; page?: string }
}) {
return (
<SearchResults
query={searchParams.q}
page={searchParams.page}
/>
);
}
// components/SearchResults.tsx (Client Component)
'use client';
interface SearchResultsProps {
query?: string;
page?: string;
}
export function SearchResults({ query, page }: SearchResultsProps) {
// Use the props instead of useSearchParams
return <div>Searching for: {query}, Page: {page}</div>;
}This pattern provides better performance and SEO while maintaining full functionality.
If you intend the route to be fully dynamic, use the connection function in the Server Component:
// app/search/page.tsx
import { connection } from 'next/server';
export default async function SearchPage({
searchParams,
}: {
searchParams: { q?: string }
}) {
await connection(); // Wait for incoming request
const query = searchParams.q;
return <div>Dynamic search: {query}</div>;
}This opts the entire route into dynamic rendering, preventing static generation issues.
After implementing your fix, run a production build to ensure there are no Suspense boundary errors:
npm run buildCheck the build output for any warnings about missing Suspense boundaries or static/dynamic rendering. A successful build confirms your search params implementation is correct.
Static vs Dynamic Rendering Impact: When useSearchParams is called in a Client Component within a statically rendered route, Next.js will client-side render the entire component tree up to the nearest Suspense boundary. This can impact your page's performance and SEO if not properly managed. Always wrap useSearchParams components in Suspense boundaries on static routes.
Layout Components Limitation: Unlike Page components, Layout components do not receive the searchParams prop. If you need search params in a layout, you must either use a Client Component with useSearchParams or pass the params down from the page component.
TypeScript Type Safety: When using the searchParams prop, define proper TypeScript types to catch missing or incorrectly named parameters at compile time. This prevents runtime errors from URL parameter mismatches.
Router Cache Behavior: The useSearchParams hook returns a ReadonlyURLSearchParams object with methods like get(), getAll(), has(), and toString(). These methods are optimized for Next.js's router cache and will trigger re-renders when search params change.
Migration from Pages Router: If you're migrating from Next.js Pages Router where useRouter().query was used everywhere, note that the App Router separates concerns: use searchParams prop in Server Components (pages) and useSearchParams hook in Client Components. This separation improves performance and enables better static optimization.
Debugging Build Errors: If you encounter "Missing Suspense boundary" errors during build but not development, it's because Next.js only enforces this requirement in production builds for static optimization. Always test with 'npm run build' before deploying.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite