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.tsximport { 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 JSX|UTF-8|Ln 15, Col 1