- Next.js
- Data Fetching & Request Lifecycle
- Breaking Change: Async Request APIs in Next.js 15+
Breaking Change: Async Request APIs in Next.js 15+
In Next.js 15+, dynamic request data—specifically params, searchParams, cookies(), and headers()—is asynchronous and must be awaited. This architectural breaking change allows Next.js to start prerendering static page shells before the server even receives the incoming HTTP request headers or URL parameters, forming the foundation for Partial Prerendering (PPR).
Learning Objectives
Understand the migration from synchronous params and searchParams to async Promises.
Refactor cookies() and headers() calls to await them in Server Components and Route Handlers.
Explain how async request APIs unlock Partial Prerendering (PPR) and earlier static shell generation.
Use React.use() to unwrap params and searchParams in Client Components.
Prerequisites & Background Knowledge
Why Interviewers Care
Critical for senior Next.js interviews. Demonstrates up-to-date knowledge of Next.js 15/16 breaking changes and architectural optimizations.
The Challenge
Why did Next.js 15 make params, searchParams, cookies(), and headers() asynchronous? Demonstrate how to access dynamic segment parameters and read cookies in both Server Components and Client Components.
Code Architecture & Implementation
1// app/products/[category]/[slug]/page.tsx2import { cookies, headers } from 'next/headers';34interface PageProps {5 // In Next.js 15+, params and searchParams are Promises!6 params: Promise<{ category: string; slug: string }>;7 searchParams: Promise<{ [key: string]: string | string[] | undefined }>;8}910export default async function ProductDetailPage({ params, searchParams }: PageProps) {11 // 1. Await route parameters12 const { category, slug } = await params;1314 // 2. Await search parameters15 const query = await searchParams;16 const sort = query.sort ?? 'featured';1718 // 3. Await headers and cookies19 const cookieStore = await cookies();20 const theme = cookieStore.get('theme')?.value ?? 'light';2122 const headerList = await headers();23 const userAgent = headerList.get('user-agent') ?? 'unknown';2425 return (26 <div>27 <h1>Product: {slug} ({category})</h1>28 <p>Sort: {String(sort)} | Theme: {theme}</p>29 </div>30 );31}
Architectural Explanation
Historically, reading cookies() or accessing params synchronously forced Next.js to wait for the incoming request before beginning any rendering work. By making these APIs return Promises, the Next.js runtime can begin prerendering static components, layouts, and Flight trees immediately. When the server reaches an unawaited async request API, only that specific branch pauses or switches to dynamic streaming. This architecture is what enables Partial Prerendering (PPR) to serve static shells in sub-10ms.
Common Mistakes & Anti-Patterns
Accessing `params.slug` without `await`, resulting in runtime warnings or `[object Promise]` rendered in HTML.
Calling `cookies().get("session")` synchronously instead of `(await cookies()).get("session")`.
Forgetting that `searchParams` on pages is also a Promise in Next.js 15+.
Interview Follow-Up Traps
Interviewers will ask: "Does making params async mean route parameters are fetched over the network?" No! Route parameters are parsed in-memory from the URL; they are async so React can defer reading them and prerender static shells before the request arrives.
Frequently Asked Questions
How do you run the automated codemod for this change?
Run `npx @next/codemod@latest next-async-request-api .` in the terminal.
How does this affect generateMetadata()?
The props passed to `generateMetadata({ params, searchParams })` are also Promises and must be awaited.
