Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by StarGuide775

How do I wrap useSearchParams() in a Suspense boundary for Next.js to fix production build errors?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

I'm encountering a production build error in my Next.js app stating that useSearchParams() must be wrapped in a Suspense boundary on the page "/find/searchResults". I've already tried wrapping my code in Suspense tags, but the error persists.

BASH
⨯ useSearchParams() should be wrapped in a suspense boundary at page "/find/searchResults". Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout at a (/home/yash/nextjs-hireme/hireme/.next/server/chunks/244.js:1:6747) at f (/home/yash/nextjs-hireme/hireme/.next/server/chunks/244.js:1:23270) at h (/home/yash/nextjs-hireme/hireme/.next/server/app/find/searchResults/page.js:1:3734) at nO (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:45959) at nI (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:47734) at nL (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:65533) at nN (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:63164) at n$ (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:46311) at nI (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:47780) at nI (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:62515) Error occurred prerendering page "/find/searchResults". Read more: https://nextjs.org/docs/messages/prerender-error Export encountered an error on /find/searchResults/page: /find/searchResults, exiting the build. ⨯ Static worker exited with code: 1 and signal: null

I’ve also reinstalled dependencies, but nothing seems to work. This page is meant to display search results fetched by the search-services component. Below is my folder structure:

PLAINTEXT
app/ ├── components/ | ├── search-services.jsx | ├── find/ │ ├── searchResults/ │ | ├── page.jsx │ ├── page.jsx

And here’s my code:

JAVASCRIPT
"use client"; import React, { useEffect, useState, Suspense } from "react"; import { useSearchParams } from "next/navigation"; import searchServices from "../../components/search-services"; import Link from "next/link"; import ServiceDetails from "../../components/service-details"; import { auth, db } from "../../firebase/config"; import { doc, getDoc } from "firebase/firestore"; import calculateDistance from "../../components/calculate-distance"; const SearchResults = () => { const searchParams = useSearchParams(); const searchTerm = searchParams.get("query").trim() || ""; const [results, setResults] = useState([]); const [loading, setLoading] = useState(true); const [selectedService, setSelectedService] = useState(null); const [userLocation, setUserLocation] = useState(null); const handleServiceClick = (service) => setSelectedService(service); const handleCloseDetails = () => setSelectedService(null); // Fetch user location from Firestore useEffect(() => { const fetchUserLocation = async () => { auth.onAuthStateChanged(async (user) => { if (user) { const docRef = doc(db, "Users", user.uid); const docSnap = await getDoc(docRef); if (docSnap.exists()) { const userData = docSnap.data(); if (userData.location) { setUserLocation(userData.location); // Assume location is { latitude, longitude } } } } else { console.log("User is not logged in"); } }); }; fetchUserLocation(); }, []); // Fetch search results useEffect(() => { const fetchResults = async () => { if (!searchTerm) return; setLoading(true); try { const services = await searchServices(searchTerm); setResults(services); } catch (error) { console.error("Error fetching search results:", error); } setLoading(false); }; fetchResults(); }, [searchTerm]); return ( <Suspense> <div className="min-h-screen bg-gray-50 px-4 py-6"> <h1 className="text-2xl font-semibold mb-4">Search Results for "{searchTerm}"</h1> <Link href="/find" className="text-teal-600 mt-4 inline-block"> Back to Search </Link> {loading ? ( <p>Loading...</p> ) : results.length === 0 ? ( <p className="text-gray-500">No services found.</p> ) : ( <div className="grid grid-cols-1 gap-4"> {results.map((service) => { const distance = userLocation && service.location ? calculateDistance(userLocation, service.location) : null; return ( <div key={service.id} className="relative p-4 border rounded-lg cursor-pointer hover:bg-gray-50 transition-colors shadow-sm" onClick={(e) => { e.stopPropagation(); handleServiceClick(service); }}> <div className="flex justify-between items-center"> <h2 className="text-lg font-semibold text-gray-800">{service.title}</h2> </div> <p className="text-gray-600 mt-2 line-clamp-2">{service.description}</p> <div className="mt-2"> <div className="flex justify-between"> <div> <p className="text-sm text-gray-500">Provider: {service.providerName}</p> <p className="text-sm text-gray-500">Phone: {service.providerPhone}</p> <p className="text-sm text-gray-500">Email: {service.providerEmail}</p> </div> {distance !== null && ( <div className="text-right"> <span className="text-md font-bold text-blue-700"> {distance.toFixed(2)} km </span> </div> )} </div> </div> <div className="mt-4 flex justify-between items-center"> <p className="text-teal-600 font-bold mb-2"> {parseFloat(service.price.min).toFixed(2)} -{parseFloat(service.price.max).toFixed(2)} </p> <span className="text-sm text-gray-500">{new Date(service.timestamp).toLocaleDateString('en-GB')}</span> </div> </div> ); })} </div> )} {/* Display selected service details */} {selectedService && ( <ServiceDetails key={selectedService.userId} userId={selectedService.userId} service={selectedService} onClose={handleCloseDetails} /> )} </div> </Suspense> ); }; export default SearchResults;

I am running Next.js 15.1.6.

1

Comments 0

Answers 3

Views 98

3 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

The error indicates that the call to useSearchParams is happening as soon as your component renders—even before react can hit the Suspense boundary. In Next.js, when a Client Component uses useSearchParams in a route that’s statically rendered, the component (or part of it) must be wrapped in a Suspense boundary so that the dynamic behavior (reading the URL) only happens client side.

One common solution is to separate the code that calls useSearchParams into its own Client Component, then have your page (a Server Component) import and wrap that component in a Suspense boundary. For example:

  1. Create a new file (e.g. SearchResultsContent.jsx) for the client-side code:

"use client";

import React, { useEffect, useState } from "react";
import { useSearchParams } from "next/navigation";
// ... other imports

const SearchResultsContent = () => {
const searchParams = useSearchParams();
const searchTerm = (searchParams.get("query") || "").trim();

const [results, setResults] = useState([]);
const [loading, setLoading] = useState(true);
const [selectedService, setSelectedService] = useState(null);
const [userLocation, setUserLocation] = useState(null);

// Your existing logic for fetching user location and search results
useEffect(() => {
// ... fetch user location code
}, []);

useEffect(() => {
const fetchResults = async () => {
if (!searchTerm) return;
setLoading(true);
try {
// Replace with your searchServices call
const services = await searchServices(searchTerm);
setResults(services);
} catch (error) {
console.error("Error fetching search results:", error);
}
setLoading(false);
};

fetchResults();

}, [searchTerm]);

return (



Search Results for "{searchTerm}"


{/* … rest of your JSX … /}
{loading ? (

Loading...


) : results.length === 0 ? (

No services found.


) : (

{results.map((service) => {
const distance =
userLocation && service.location
? calculateDistance(userLocation, service.location)
: null;
return (
<div
key={service.id}
className="relative p-4 border rounded-lg cursor-pointer hover:bg-gray-50 transition-colors shadow-sm"
onClick={(e) => {
e.stopPropagation();
setSelectedService(service);
}}
>
{/
… service details … /}

);
})}

)}
{/
Render ServiceDetails if a service is selected */}
{selectedService && (
<ServiceDetails
key={selectedService.userId}
userId={selectedService.userId}
service={selectedService}
onClose={() => setSelectedService(null)}
/>
)}

);
};

export default SearchResultsContent;

  1. In your page component (app/find/searchResults/page.jsx), remove the "use client" directive so it becomes a Server Component. Then wrap the client component inside a Suspense boundary:

import React, { Suspense } from "react";
import SearchResultsContent from "../../../components/SearchResultsContent"; // adjust path if needed

export default function SearchResultsPage() {
return (
<>
<Suspense fallback={
Loading search results...
}>


</>
);
}

In this pattern:

• The Server Component (SearchResultsPage) is rendered during pre-render and wraps the client code in a Suspense boundary.

• The client-side code inside SearchResultsContent is the only one that calls useSearchParams and it’s now safely inside a Suspense boundary.

This change should resolve the production build error since the dynamic behavior is now confined to a client component that’s rendered within a Suspense boundary, following Next.js best practices.

Make sure to update your import paths as necessary and remove "use client" from the page file so it remains a server component.

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by StarExplorer036 1 month ago

0

Solved it by by calling the SearchResults component inside the Page component and wrapping it in a Suspense block.

JAVASCRIPT
import { Suspense } from "react"; const searchResults = () => { // code } const Page = () => { return ( <Suspense> <searchResults /> </Suspense> ) } export default Page

No comments yet.

Answer by MercurialNomad493 1 month ago

0

I would not recommend rendering the whole page client side, but if you want to keep it this way you can do this:

```plaintext
app/
├── find/
│ ├── searchResults/
│ │ ├── layout.tsx
│ | ├── page.jsx

<br><br>in the `layout.tsx` you can use suspense component and wrap children prop inside:<br><br>```typescript
import { Suspense } from "react";
export default function SRLayout({ children }: { children: React.ReactNode }) {
   return (
      <Suspense>
         {children}
      </Suspense>
   )
}



This approach is not ideal since it beats the whole point of partial prerendering and server side rendering.

No comments yet.

Discussion

No comments yet.