Dynamic routes, params and searchParams

Next.js · Essentials · 11 min

Blogs, products and user pages are all dynamic routes; params handling changed in recent versions.

Wrap a folder name in square brackets to create a dynamic segment: app/blog/[slug]/page.tsx matches /blog/hello-world.

  • [...slug] is a catch-all: /docs/a/b/c gives slug = ['a', 'b', 'c'].
  • [[...slug]] is an optional catch-all that also matches /docs.

Pages receive `params` and `searchParams` props, and both are Promises (since Next.js 15). Await them in an async Server Component:

export default async function Page({ params }: PageProps<'/blog/[slug]'>) {
  const { slug } = await params
}

PageProps<'/route'> and LayoutProps<'/route'> are global type helpers generated from your routes (by next dev, next build or next typegen), so params are typed without manual annotations. Layouts receive params but not searchParams.

In Client Components, read the promise with React's use(params), or use the useParams() and useSearchParams() hooks.

Params are always strings (or string arrays for catch-alls); convert and validate them yourself. Call notFound() when the slug doesn't exist.

Values of dynamic params are only known at request time unless you list them with `generateStaticParams`, which lets Next.js prerender those pages at build time. You will use it when you learn about prerendering.

Example

// app/shop/[category]/[id]/page.tsx  ->  /shop/shoes/42?color=red
import { notFound } from 'next/navigation'

const products: Record<string, { name: string }> = { '42': { name: 'Trail runner' } }

export default async function ProductPage({
  params,
  searchParams,
}: PageProps<'/shop/[category]/[id]'>) {
  const { category, id } = await params            // { category: 'shoes', id: '42' }
  const { color = 'black' } = await searchParams   // color: 'red'
  const product = products[id]
  if (!product) notFound()                         // renders the not-found UI

  return (
    <h1>
      {product.name} in {String(color)} ({category})
    </h1>
  )
}

// app/docs/[...slug]/page.tsx: /docs/a/b -> (await params).slug = ['a', 'b']

Remember

  • [slug], [...slug], [[...slug]] create dynamic segments
  • params and searchParams are Promises: await them (or use() in client code)
  • PageProps<'/route'> types them automatically

Watch out for

  • Accessing params.slug without awaiting; in Next.js 15+ params is a Promise.

Check yourself

  1. For the file app/docs/[...slug]/page.tsx, what is the awaited params.slug for /docs/guides/intro?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: nextjs.org/docs/app/api-reference/file-conventions/dynamic-routes