Next.js 15 introduces a breaking change by transitioning several request-specific APIs to asynchronous operations. This shift enhances performance and lays the groundwork for future optimizations. The affected APIs include cookies(), headers(), draftMode(), and route-specific parameters such as params and searchParams. This article covers these changes in detail and provides updated code examples to help developers migrate seamlessly.
The transition to asynchronous request APIs allows the Next.js server to prepare responses more efficiently before requests arrive, improving overall performance. It also aligns with modern best practices for handling I/O-heavy operations, such as database queries, authentication checks, and dynamic content generation.
🍪 cookies() API (Now Asynchronous)
The cookies() API, used for managing cookies in Next.js, is now asynchronous.
🔹Key Changes:
📩 headers() API (Now Asynchronous)
The headers() API, previously synchronous, must now be awaited.
🔹Key Changes:
📝 draftMode() API (Now Asynchronous)
The draftMode() API for previewing draft content must also be awaited.
🔹Key Changes:
🔀 params in Route Handlers (Now Asynchronous)
In Next.js 15, params in layout.js, page.js, route.js, default.js, generateMetadata, and generateViewport are now asynchronous.
🔹Key Changes:
🔎 params & searchParams (Now Asynchronous)
params and searchParams are API's that have transitioned to asynchronous behavior.
🔹Key Changes:
To assist with the transition, Next.js provides an automated codemod to refactor your code:
This tool updates affected API calls to their new asynchronous versions automatically. However, developers should manually review changes for edge cases.
The shift to asynchronous request APIs in Next.js 15 significantly improves efficiency, especially for applications relying on headers, cookies, and dynamic route parameters. Developers should update their projects accordingly and leverage the provided codemod for a smoother migration. By adopting these changes, Next.js applications will be better optimized for modern web performance needs.
1// app/page.tsx2import { cookies } from 'next/headers';34export async function AdminPanel() {5const cookieStore = await cookies(); // Await is now required6const token = cookieStore.get('token');78return token ? 'Authenticated' : 'Not Authenticated';9}10
1import { headers } from 'next/headers'23// 🟡 Before4const headersList = headers()5const userAgent = headersList.get('user-agent')67// ✅ After8const headersList = await headers()9const userAgent = headersList.get('user-agent')10
1import { draftMode } from 'next/headers'23// 🟡 Before4const { isEnabled } = draftMode()56// ✅ After7const { isEnabled } = await draftMode()8}9
1// app/api/route.ts23// 🟡 Before4type Params = { slug: string }56export async function GET(request: Request, segmentData: { params: Params }) {7const params = segmentData.params8const slug = params.slug9}1011// ✅ After12type Params = Promise<{ slug: string }>1314export async function GET(request: Request, segmentData: { params: Params }) {15const params = await segmentData.params16const slug = params.slug17}18
1// app/layout.tsx23type Params = Promise<{ slug: string }>45export async function generateMetadata({ params }: { params: Params }) {6const { slug } = await params7}89export default async function Layout({10children,11params,12}: {13children: React.ReactNode14params: Params15}) {16const { slug } = await params17}181920// app/page.tsx2122type Params = Promise<{ slug: string }>23type SearchParams = Promise<{ [key: string]: string | string[] | undefined }>2425export async function generateMetadata(props: {26params: Params27searchParams: SearchParams28}) {29const params = await props.params30const searchParams = await props.searchParams31const slug = params.slug32const query = searchParams.query33}3435export default async function Page(props: {36params: Params37searchParams: SearchParams38}) {39const params = await props.params40const searchParams = await props.searchParams41const slug = params.slug42const query = searchParams.query43}44
1// app/layout.tsx23import { use } from 'react'45type Params = Promise<{ slug: string }>67export default function Layout(props: {8children: React.ReactNode9params: Params10}) {11const params = use(props.params)12const slug = params.slug13}141516// app/page.tsx1718import { use } from 'react'1920type Params = Promise<{ slug: string }>21type SearchParams = Promise<{ [key: string]: string | string[] | undefined }>2223export default function Page(props: {24params: Params25searchParams: SearchParams26}) {27const params = use(props.params)28const searchParams = use(props.searchParams)29const slug = params.slug30const query = searchParams.query31}32
12npx @next/codemod@canary next-async-request-api3