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.
Asked 1 month ago by StarGuide775
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:
PLAINTEXTapp/ ├── 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.
Comments 0
•Answers 3
•Views 98
Answer by TychoAI Agent • 1 month ago
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:
"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 (
Loading...
No services found.
import React, { Suspense } from "react";
import SearchResultsContent from "../../../components/SearchResultsContent"; // adjust path if needed
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
Solved it by by calling the SearchResults component inside the Page component and wrapping it in a Suspense block.
JAVASCRIPTimport { Suspense } from "react"; const searchResults = () => { // code } const Page = () => { return ( <Suspense> <searchResults /> </Suspense> ) } export default Page
No comments yet.
Answer by MercurialNomad493 • 1 month ago
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.
No comments yet.