useSearchParams() & suspense boundary

Used in this guide:
Next.js 15.3.3
Vercel

Error:

useSearchParams() should be wrapped in a suspense boundary at page '/payment-success'. Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout


Reason

When using useSearchParams() inside a Server Component (like a page.tsx) in Next.js 13+ with App Router, you might run into this error

This happens even when you've added 'use client' and wrapped your component with <Suspense>. Here’s why it happens and how to fix it properly.

What Causes the Error?

  • You’re calling useSearchParams() directly in a page.tsx, which by default is a Server Component.
  • Even if you add 'use client' at the top and wrap parts in <Suspense>, the page is still considered a Server Component, and trying to use useSearchParams() forces a Client-Side Rendering (CSR) bailout.
  • Vercel throws this warning because you're mixing Server and Client logic incorrectly.

The Proper Fix

  • Keep your page.tsx as a Server Component (no "use client" at the top).
  • Move the logic that uses useSearchParams() into a separate Client Component.
  • Import that component into your page.tsx and wrap it with <Suspense> if needed.

Example (incorrect version)

Incorrect Version
'use client';
import { useSearchParams } from 'next/navigation';
 
export default function PaymentSuccessPage() {
    const params = useSearchParams();
    const status = params.get('status');
    return <div>Payment status: {status}</div>;
}

After (Correct Version with Separated Client Component)

app/payment-success/page.tsx
import { Suspense } from 'react';
import PaymentStatusClient from './PaymentStatusClient';
 
export default function PaymentSuccessPage() {
  return (
    <Suspense fallback={<div>Loading status...</div>}>
      <PaymentStatusClient />
    </Suspense>
  );
}
app/payment-success/PaymentStatusClient.tsx
'use client';
import { useSearchParams } from 'next/navigation';
 
export default function PaymentStatusClient() {
  const params = useSearchParams();
  const status = params.get('status');
  return <div>Payment status: {status}</div>;
}

References:

JKT

Stay focused, and the rest will follow

©Jakkrit Turner. All rights reserved