Await request APIs

In Next.js 15, params, cookies() and headers() are Promises.

From next-best-practices, async-patterns.md.

Type it, run it

Edit the lines; try deleting await on the right.

Read like an objectBad
export default async function Page({ params }) {
  return <h1 className={theme}>{slug}</h1>
}
AwaitedGood
export default async function Page({ params }) {
  return <h1 className={theme}>{slug}</h1>
}
// app/blog/[slug]/page.tsx
import { cookies } from 'next/headers'

type Props = {
  params: Promise<{ slug: string }>
  searchParams: Promise<{ query?: string }>
}

export default async function Page({ params, searchParams }: Props) {
  const { slug } = await params
  const { query } = await searchParams
  const theme = (await cookies()).get('theme')?.value
  return <h1 className={theme}>{slug}</h1>
}

Sync component: use(params)

Click a cell to switch component and unwrap.

page.tsxServer

      
Renders/blog/

Componentparamsawaituse()
import { use } from 'react'

type Props = { params: Promise<{ slug: string }> }

export default function Page({ params }: Props) {
  const { slug } = use(params)
  return <h1>{slug}</h1>
}

Run the codemod

Run it, then switch views.

View
    app/blog/[slug]/page.tsx

    Why it matters, pros and cons

    Why it matters

    Request data exists per request, so Next.js hands it over as a Promise.

    Pros

    • A sync read becomes a type error.
    • One pattern for pages, layouts and handlers.
    • A codemod does most of the migration.

    Cons

    • await params.slug awaits undefined; write (await params).slug.
    • Sync components can't await; use use().
    • Reading cookies() or headers() makes the render dynamic.