javascript - useSearchParams() should be wrapped in a suspense boundary at page "findsearchResults" - Stack Ov

admin2025-04-17  4

Ive tried wrapping my code in Suspense Tags but Im still getting the following error while building production build of my nextjs app.

 ⨯ useSearchParams() should be wrapped in a suspense boundary at page "/find/searchResults". Read more: 
    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: 
Export encountered an error on /find/searchResults/page: /find/searchResults, exiting the build.
 ⨯ Static worker exited with code: 1 and signal: null

Ive also tried reinstalling the dependencies but nothing works. This page displays the search results fetched by the search-services component. Here's my folder structure:

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

Here's my code:

"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

Ive tried wrapping my code in Suspense Tags but Im still getting the following error while building production build of my nextjs app.

 ⨯ 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

Ive also tried reinstalling the dependencies but nothing works. This page displays the search results fetched by the search-services component. Here's my folder structure:

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

Here's my code:

"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

Share Improve this question asked Feb 1 at 18:09 Yash KumarYash Kumar 217 bronze badges 0
Add a comment  | 

2 Answers 2

Reset to default 3

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

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

in the layout.tsx you can use suspense component and wrap children prop inside:

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.

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

import { Suspense } from "react";

const searchResults = () => {
    // code
}

const Page = () => {
    return (
        <Suspense>
            <searchResults />
        </Suspense>
    )
}

export default Page
转载请注明原文地址:http://anycun.com/QandA/1744820913a88077.html