Dynamic Routes — [slug]

Square Brackets Make a Segment Variable

Dynamic Routes — [slug]

A folder name wrapped in square brackets captures that segment as a route parameter the page can read.

4 min read Level 2/5 #nextjs#dynamic-routes
What you'll learn
  • Create `app/blog/[slug]/page.tsx`
  • Read `params` inside the page component
  • Use `generateStaticParams` for static generation

A dynamic route lets one folder match many URLs. Wrap the folder name in square brackets, and Next.js passes the captured value to your page.

A Dynamic Segment

app/blog/[slug]/page.tsx
# matches /blog/hello, /blog/intro-to-rust, /blog/anything

The page receives a params object whose key is the bracket name.

// app/blog/[slug]/page.tsx
export default async function PostPage({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  return <h1>Post: {slug}</h1>;
}

In Next.js 15, params is a Promise. Always await it before destructuring.

Multiple Dynamic Segments

You can nest dynamic folders.

app/[shop]/[item]/page.tsx
# /acme/widget → params = { shop: 'acme', item: 'widget' }
// app/[shop]/[item]/page.tsx
export default async function ItemPage({
  params,
}: {
  params: Promise<{ shop: string; item: string }>;
}) {
  const { shop, item } = await params;
  return (
    <p>
      Shop {shop}, item {item}
    </p>
  );
}

Static Generation with generateStaticParams

To pre-render dynamic routes at build time, export generateStaticParams. It returns an array of params objects, and Next.js builds one page per entry.

// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts: Array<{ slug: string }> = await fetch(
    'https://api.example.com/posts',
  ).then((r) => r.json());

  return posts.map((p) => ({ slug: p.slug }));
}

export default async function PostPage({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const post = await fetchPost(slug);
  return <article>{post.title}</article>;
}

Unknown slugs fall back to on-demand rendering by default. The next lesson covers catch-all segments for matching many path parts at once.

Catch-All Routes →