← Back to chapters
06intermediate

Dynamic Routes & Route Groups

Params, catch-all routes, optional segments, and organizing routes with groups.

Dynamic Routes: URLs with Parameters

Static routes are great, but real apps need dynamic URLs. Blog posts, user profiles, product pages. The URL contains a variable part. Next.js handles this with bracket notation [param].

Basic Dynamic Route

src/app/blog/[slug]/page.tsx
tsx
1// src/app/blog/[slug]/page.tsx
2// Matches: /blog/hello-world, /blog/nextjs-guide, etc.
3
4export default async function BlogPost({
5params,
6}: {
7params: Promise<{ slug: string }>;
8}) {
9const { slug } = await params;
10
11return (
12 <article>
13 <h1>Post: {slug}</h1>
14 </article>
15);
16}
⚠️

Next.js 15 change

In Next.js 15, params is now a Promise. You need to await it. This was a breaking change from v14 where params was a regular object. Don't forget the async/await!

Catch-All Routes

Need to catch multiple path segments? Use [...param] for catch-all or [[...param]] for optional catch-all:

src/app/docs/[...slug]/page.tsx
tsx
1// src/app/docs/[...slug]/page.tsx
2// Matches: /docs/intro, /docs/api/auth, /docs/api/auth/oauth
3
4export default async function DocsPage({
5params,
6}: {
7params: Promise<{ slug: string[] }>;
8}) {
9const { slug } = await params;
10// slug = ["api", "auth", "oauth"] for /docs/api/auth/oauth
11
12return (
13 <div>
14 <p>Path: {slug.join(" > ")}</p>
15 </div>
16);
17}
src/app/shop/[[...categories]]/page.tsx
tsx
1// src/app/shop/[[...categories]]/page.tsx
2// Matches: /shop, /shop/electronics, /shop/electronics/phones
3// The [[...]] makes it OPTIONAL, so /shop alone also works
4
5export default async function ShopPage({
6params,
7}: {
8params: Promise<{ categories?: string[] }>;
9}) {
10const { categories } = await params;
11
12if (!categories) {
13 return <h1>All Products</h1>;
14}
15
16return <h1>Category: {categories.join(" > ")}</h1>;
17}

Route Groups (Parentheses)

Remember from the layouts chapter, parentheses () create groups that don't affect the URL:

Route groups for different layouts
text
1src/app/
2├── (auth)/
3│ ├── layout.tsx ← Auth layout (centered card)
4│ ├── login/page.tsx → /login
5│ └── register/page.tsx → /register
6├── (app)/
7│ ├── layout.tsx ← App layout (sidebar + header)
8│ ├── dashboard/page.tsx → /dashboard
9│ └── settings/page.tsx → /settings
🚀

Pattern I use often

I group my routes by layout:
• (marketing) for landing pages with a simple header
• (app) for the authenticated dashboard with a sidebar
• (auth) for login/register with a centered card layout
Clean separation, clean URLs.

Watch and Learn