CPCODELAB
Next.js

Dynamic Routes and Reading Params

11 min read

Creating Dynamic URL Segments

Wrap a folder name in square brackets to create a dynamic segment — a wildcard that matches any value. The matched value is passed as a prop to the page component.

text
src/app/
  blog/
    [slug]/
      page.tsx    -> /blog/hello-world, /blog/nextjs-tips, ...

In modern Next.js (14+), the params prop is asynchronous and must be awaited before you can read from it. This is a breaking change from older versions — forgetting to await params is a very common source of bugs.

tsx
// src/app/blog/[slug]/page.tsx
type Props = { params: Promise<{ slug: string }> };

export default async function BlogPostPage({ params }: Props) {
  const { slug } = await params; // MUST await params
  const post = await fetchPostBySlug(slug);

  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.body}</p>
    </article>
  );
}

async function fetchPostBySlug(slug: string) {
  const res = await fetch(`https://api.example.com/posts/${slug}`, {
    next: { revalidate: 3600 },
  });
  if (!res.ok) throw new Error("Post not found");
  return res.json();
}

Catch-All Segments

Use [...slug] for a catch-all that matches /docs/a, /docs/a/b, /docs/a/b/c and so on. Use [[...slug]] for an optional catch-all that also matches /docs.

Always type params as Promise<{ ... }> and await it. Accessing params.slug synchronously without awaiting will produce a deprecation warning in Next.js 14 and may break in Next.js 15+.

Ready to test yourself?
Practice Next.js— quiz & coding exercises