Suspense Boundaries & Gotchas
Conceptparamsync > concepts

Suspense Boundaries & Gotchas

Ensuring Next.js App Router static optimization and preventing hydration mismatches when reading URL state.

The Problem

In Next.js App Router, using hooks that access client-side search parameters (such as useSearchParams() or useQueryState()) directly inside a page causes Next.js to de-optimize static generation (SSG) and opt into dynamic client-side rendering for the entire route unless wrapped in a React Suspense boundary.

The paramsync Solution

Always wrap components that call useQueryState or useBatchQueryParams inside a <Suspense fallback={...}> boundary, or place the Suspense boundary at the parent route level. This permits Next.js to statically optimize the outer shell while streaming or hydrating the query-driven component.

Key Architectural Takeaways

  • Preserves Next.js static page generation and Turbo prefetching.
  • Prevents SSR hydration mismatch errors.
  • Provides instantaneous loading skeleton fallback while search params initialize.

Implementation Pattern

TSPattern.tsx
// app/catalog/page.tsx
import { Suspense } from 'react';
import { CatalogFeed } from '@/features/catalog/components/CatalogFeed';
 
export default function CatalogPage() {
return (
<main>
<h1>Product Catalog</h1>
{/* Required Suspense boundary for useQueryState */}
<Suspense fallback={<div>Loading filters...</div>}>
<CatalogFeed />
</Suspense>
</main>
);
}
main|paramsync
TypeScript JSXLn 15, Col 1
;