← All lessons
Lesson 10 · Intermediate

Dynamic Routes

Capture URL parameters with bracket folders. Build product pages, blog posts and user profiles without listing them all.

Static routes only get you so far. For a blog you don't want to write a page for every post — you want one template that reads the slug from the URL. The App Router does this with bracket-named folders.

A single dynamic segment

app/blog/[slug]/page.js     → /blog/hello, /blog/world, …

Inside the page, you receive the captured value through the params prop. In Next.js 16 params is a Promise that you must await:

// app/blog/[slug]/page.js
export default async function PostPage({ params }) {
  const { slug } = await params;
  const post = await getPost(slug);
  return <article>{post.body}</article>;
}

Catch-all and optional catch-all

  • **[...path]** — catches one or more segments. params.path is an array like ['a', 'b'].
  • **[[...path]]** — same, but also matches the base URL with no path at all.
app/docs/[...slug]/page.js     → /docs/a, /docs/a/b/c (matches anything below /docs)
app/shop/[[...filters]]/page.js → /shop AND /shop/red/large

Pre-generating dynamic pages

If you know the set of values ahead of time, you can pre-render them at build time using generateStaticParams:

// app/blog/[slug]/page.js
export async function generateStaticParams() {
  const posts = await getAllPosts();
  return posts.map((p) => ({ slug: p.slug }));
}

export default async function PostPage({ params }) {
  const { slug } = await params;
  // ...
}

Pages whose params come from generateStaticParams are built as static HTML — no server work at request time. Anything not in the list still renders on demand by default.

Reading search params

Query strings like ?q=react&page=2 arrive in a separate prop called searchParams, also a Promise:

export default async function SearchPage({ searchParams }) {
  const { q = "" } = await searchParams;
  const results = await search(q);
  return <Results items={results} />;
}
Note: Reading searchParams opts the page out of static rendering, since they change per request. That's usually what you want.

Try it

Wire up a dynamic product page that reads the `id` from the URL and passes it to `getProduct`.

Need a hint?

The folder name in brackets matches the prop key, e.g. [id] → params.id.

Quiz

Pick the best answer. You only get one shot per question.

1. Which file matches both /docs and /docs/getting-started?

2. In Next.js 16, what is the type of `params` inside a page?

3. What does `generateStaticParams` do?