Build Gagal Prerender? Cara Mengatasi useSearchParams Suspense Next Js

Pisahkan komponen yang memanggil useSearchParams() ke Client Component tersendiri, lalu bungkus komponen itu dengan <Suspense> di page induk. Itu memperbaiki error build missing suspense boundary karena Next.js bisa menampilkan fallback saat prerender tanpa membaca query string.

Kenapa useSearchParams Wajib Dibungkus Suspense

useSearchParams() membaca query dinamis di browser, misalnya ?q=sepatu. Nilainya belum ada saat Next.js melakukan prerender statis di server.

App Router menghentikan build kalau hook ini dipanggil tanpa boundary. Pesan umumnya muncul sebagai useSearchParams() should be wrapped in a suspense boundary saat next build atau saat halaman di prerender.

  • Page statis mencoba di-render sekali saat build, jadi query harus ditunda sampai client.
  • Page dinamis tetap butuh Suspense agar bagian query bisa streaming.
  • Solusinya bukan menghapus prerender, melainkan mengisolasi bagian yang membaca query.

Tabel Penyebab dan Solusi Cepat

Langkah diagnosis yang benar:

1. Baca pesan error build sampai selesai.
2. Cari semua file yang memanggil useSearchParams.
3. Periksa apakah pemanggilnya sudah berada di dalam Suspense.

Masalah Penyebab Solusi
Hook dipakai langsung di page Page ikut prerender Pindah ke komponen anak
Sudah Client Component tapi error Tanpa boundary Bungkus dengan Suspense
Seluruh halaman ikut loading Suspense terlalu tinggi Bungkus hanya bagian query

Cara Memperbaiki dengan Suspense Boundary

1. Buat komponen "use client" khusus untuk membaca query.
2. Import dan bungkus komponen itu dengan Suspense di parent Server Component.
3. Beri fallback kecil, misalnya skeleton filter atau teks memuat.

// components/SearchFilter.tsx
"use client";
import { useSearchParams } from "next/navigation";

export default function SearchFilter() {
  const params = useSearchParams();
  const q = params.get("q") ?? "";
  return <p>Kata kunci: {q}</p>;
}

// app/produk/page.tsx
import { Suspense } from "react";
import SearchFilter from "@/components/SearchFilter";

export default function ProdukPage() {
  return (
    <main>
      <h1>Produk</h1>
      <Suspense fallback={<p>Memuat filter...</p>}>
        <SearchFilter />
      </Suspense>
    </main>
  );
}
  • Jangan menaruh Suspense di komponen yang sama dengan useSearchParams, taruh di parent.
  • Buat fallback sekecil mungkin supaya hanya area filter yang berkedip.
  • Jangan langsung memakai force-dynamic hanya untuk menghilangkan error ini.

FAQ

Apakah semua page pakai useSearchParams harus Suspense?

Ya, setiap pemakaian useSearchParams() di App Router butuh boundary Suspense di atasnya.

Di mana menaruh Suspense agar tidak loading satu halaman?

Bungkus hanya komponen kecil yang membaca query, bukan seluruh layout page.

Apakah use client bisa menggantikan Suspense?

Tidak, Client Component tetap di-prerender sehingga boundary Suspense tetap wajib.