Skip to the solution
MediumFrontend4 min read

App Router 15 — why are params and searchParams now Promises?

897 views

The problem

Question presented to candidate: "After upgrading, TypeScript complains that params is a Promise in every page. Why did Next.js make params and searchParams async, and what happens if you still read them synchronously?"

What a strong answer should cover:

  • Since Next.js 15, params and searchParams in pages, layouts, generateMetadata and Route Handlers are Promises you must await (or unwrap with use() in a client component).
  • The same release made cookies(), headers() and draftMode() async.
  • The reason: Next.js can start rendering (and prerender the parts that do not need request data) before the request-specific values are resolved, which is what enables streaming and PPR.
  • Next.js 15 kept a temporary synchronous fallback with a warning; in 16 synchronous access is gone and reading a property of the Promise gives undefined.
  • A codemod (next-async-request-api) rewrites most code automatically.

Clarifying questions expected:

  • "Is this a Server or Client Component?" — await in server code, use() in client code, or useParams() from next/navigation.
  • "Which Next version are you upgrading to?" — 16 has no sync fallback.

Code / implementation expected: Yes — the async signature in a page and in a client component.

  • app-router
  • nextjs-15
  • async-params

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

4 min read · 8 sections

Read the code

Awaiting params in a page, unwrapping them in a client page
// app/blog/[slug]/page.tsx  (Server Component)
export default async function Post({
  params,
  searchParams,
}: {
  params: Promise<{ slug: string }>;
  searchParams: Promise<{ ref?: string }>;
}) {
  const { slug } = await params;
  const { ref } = await searchParams;
  return <h1>{slug} {ref ? `(from ${ref})` : null}</h1>;
}

// app/tools/[id]/page.tsx  (Client Component page)
"use client";
import { use } from "react";
export default function Tool({ params }: { params: Promise<{ id: string }> }) {
  const { id } = use(params);
  return <p>Tool {id}</p>;
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 43 of 129 in Next.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Next.js questions
Up nextMedium

How do you revalidate data after a mutation in Next.js?

Continue