After upgrading my application to Next.js 15 (Release Candidate / Stable) and React 19, my dynamic routing pages crashed with the runtime error:
Error: Route "/blog/[slug]" used `params.slug`. `params` should be awaited before using its properties.
Learn more: https://nextjs.org/docs/messages/sync-dynamic-apisPreviously in Next.js 14 App Router, dynamic route props were accessed directly as synchronous objects: export default function Page({ params }: { params: { slug: string } }) { return <div>{params.slug}</div>; }. Why was this changed to asynchronous promises in Next.js 15, and what is the exact migration pattern for both Server Components and Client Components?
Direct Technical Solution: In Next.js 15, dynamic route parameters (
params) and search parameters (searchParams) transitioned from synchronous plain JavaScript objects to native Promises. This breaking change was implemented to support React 19 Server Components and the new Partial Prerendering (PPR) model, where the server renders the static page skeleton before dynamic parameters resolve.1. Server Component Migration (Async/Await)
In all Next.js 15 Server Components (the default in the
app/directory), typeparamsas aPromiseand explicitlyawaitit before accessing any key:2. Client Component Migration (React 19 `use()` Hook)
If your page file uses
"use client", you cannot make the component functionasync. Instead, unwrap theparamsPromise using React 19’s nativeReact.use()hook:3. Comparison: Next.js 14 vs Next.js 15
{ slug: string }(Synchronous Object)Promise<{ slug: string }>(Async Promise)params.slug)const { slug } = await params;useParams()hookuse(params)oruseParams()