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
Suspenseagar 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
Suspensedi komponen yang sama denganuseSearchParams, taruh di parent. - Buat fallback sekecil mungkin supaya hanya area filter yang berkedip.
- Jangan langsung memakai
force-dynamichanya 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.